diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1fb02ee9..d6c39482 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+Version 101:
+
+* Refactor all examples
+
+--------------------------------------------------------------------------------
+
Version 100:
* Fix doc convenience includes
diff --git a/build/build-and-test.sh b/build/build-and-test.sh
index c24317bb..456e7f9e 100755
--- a/build/build-and-test.sh
+++ b/build/build-and-test.sh
@@ -97,7 +97,9 @@ INC_DIR="$BOOST_ROOT/boost/beast"
function build_bjam ()
{
- if [[ $VARIANT == "coverage" ]]; then
+ if [[ $VARIANT == "coverage" ]] || \
+ [[ $VARIANT == "valgrind" ]] || \
+ [[ $VARIANT == "ubasan" ]]; then
bjam \
libs/beast/test/beast/core//fat-tests \
libs/beast/test/beast/http//fat-tests \
diff --git a/doc/images/server.png b/doc/images/server.png
deleted file mode 100644
index fc609800..00000000
Binary files a/doc/images/server.png and /dev/null differ
diff --git a/doc/qbk/00_main.qbk b/doc/qbk/00_main.qbk
index e138c0e3..45453063 100644
--- a/doc/qbk/00_main.qbk
+++ b/doc/qbk/00_main.qbk
@@ -85,8 +85,8 @@
[import ../../example/common/detect_ssl.hpp]
[import ../../example/doc/http_examples.hpp]
[import ../../example/echo-op/echo_op.cpp]
-[import ../../example/http-client/http_client.cpp]
-[import ../../example/websocket-client/websocket_client.cpp]
+[import ../../example/http/client/sync/http_client_sync.cpp]
+[import ../../example/websocket/client/sync/websocket_client_sync.cpp]
[import ../../include/boost/beast/http/basic_file_body.hpp]
diff --git a/doc/qbk/02_examples.qbk b/doc/qbk/02_examples.qbk
index 1f01da8a..7ea8a8ab 100644
--- a/doc/qbk/02_examples.qbk
+++ b/doc/qbk/02_examples.qbk
@@ -14,13 +14,11 @@ These complete programs are intended to quickly impress upon readers
the flavor of the library. Source code and build scripts for them are
located in the example/ directory.
-
-
[section HTTP Client]
Use HTTP to make a GET request to a website and print the response:
-File: [repo_file example/http-client/http_client.cpp]
+File: [repo_file example/http/client/sync/http_client_sync.cpp]
[example_http_client]
@@ -30,7 +28,7 @@ File: [repo_file example/http-client/http_client.cpp]
Establish a WebSocket connection, send a message and receive the reply:
-File: [repo_file example/websocket-client/websocket_client.cpp]
+File: [repo_file example/websocket/client/sync/websocket_client_sync.cpp]
[example_websocket_client]
@@ -44,85 +42,196 @@ File: [repo_file example/websocket-client/websocket_client.cpp]
[block'''''']
Source code and build scripts for these programs are located
-in the example/ directory.
+in the [repo_file example] directory.
-[section HTTP Crawl]
+[template example_src[path name] ''''''[name]'''''']
-This example retrieves the page at each of the most popular domains
-as measured by Alexa.
+[heading Clients]
-* [repo_file example/http-crawl/http_crawl.cpp]
+These HTTP clients submit a GET request to a server specified on the command
+line, and prints the resulting response. The crawl client asynchronously
+fetches the document root of the 10,000 top ranked domains, this may be
+used to evaluate robustness.
-[endsect]
+[table
+[[Description] [Source File] [Source File (using SSL)]]
+[
+ [HTTP, synchronous]
+ [[example_src example/http/client/sync/http_client_sync.cpp http_client_sync.cpp]]
+ [[example_src example/http/client/sync-ssl/http_client_sync_ssl.cpp http_client_sync_ssl.cpp]]
+][
+ [HTTP, asynchronous]
+ [[example_src example/http/client/async/http_client_async.cpp http_client_async.cpp]]
+ [[example_src example/http/client/async-ssl/http_client_async_ssl.cpp http_client_async_ssl.cpp]]
+][
+ [HTTP, coroutine]
+ [[example_src example/http/client/coro/http_client_coro.cpp http_client_coro.cpp]]
+ [[example_src example/http/client/coro-ssl/http_client_coro_ssl.cpp http_client_coro_ssl.cpp]]
+][
+ [HTTP crawl (asynchronous)]
+ [[example_src example/http/client/crawl/http_crawl.cpp http_crawl.cpp]]
+ []
+]]
+These WebSocket clients connect to a
+server and send a message, then receive a message and print the response
+before disconnecting.
+[table
+[[Description] [Source File] [Source File (using SSL)]]
+[
+ [WebSocket, synchronous]
+ [[example_src example/websocket/client/sync/websocket_client_sync.cpp websocket_client_sync.cpp]]
+ [[example_src example/websocket/client/sync-ssl/websocket_client_sync_ssl.cpp websocket_client_sync_ssl.cpp]]
+][
+ [WebSocket, asynchronous]
+ [[example_src example/websocket/client/async/websocket_client_async.cpp websocket_client_async.cpp]]
+ [[example_src example/websocket/client/async-ssl/websocket_client_async_ssl.cpp websocket_client_async_ssl.cpp]]
+][
+ [WebSocket, coroutine]
+ [[example_src example/websocket/client/coro/websocket_client_coro.cpp websocket_client_coro.cpp]]
+ [[example_src example/websocket/client/coro-ssl/websocket_client_coro_ssl.cpp websocket_client_coro_ssl.cpp]]
+]]
-[section HTTP Client (with SSL)]
+[heading Servers]
-This example demonstrates sending and receiving HTTP messages
-over a TLS connection. Requires OpenSSL to build.
+These HTTP servers deliver files from a root directory specified on the
+command line.
-* [repo_file example/http-client-ssl/http_client_ssl.cpp]
+[table
+[[Description] [Source File] [Source File (using SSL)]]
+[
+ [HTTP, synchronous]
+ [[example_src example/http/server/sync/http_server_sync.cpp http_server_sync.cpp]]
+ [[example_src example/http/server/sync-ssl/http_server_sync_ssl.cpp http_server_sync_ssl.cpp]]
+][
+ [HTTP, asynchronous]
+ [[example_src example/http/server/async/http_server_async.cpp http_server_async.cpp]]
+ [[example_src example/http/server/async-ssl/http_server_async_ssl.cpp http_server_async_ssl.cpp]]
+][
+ [HTTP, coroutine]
+ [[example_src example/http/server/coro/http_server_coro.cpp http_server_coro.cpp]]
+ [[example_src example/http/server/coro-ssl/http_server_coro_ssl.cpp http_server_coro_ssl.cpp]]
+][
+ [HTTP, stackless coroutine]
+ [[example_src example/http/server/stackless/http_server_stackless.cpp http_server_stackless.cpp]]
+ [[example_src example/http/server/stackless-ssl/http_server_stackless_ssl.cpp http_server_stackless_ssl.cpp]]
+][
+ [HTTP, fast (optimized for speed)]
+ [[example_src example/http/server/fast/http_server_fast.cpp http_server_fast.cpp]]
+ []
+][
+ [HTTP, small (optimized for space)]
+ [[example_src example/http/server/small/http_server_small.cpp http_server_small.cpp]]
+ []
+][
+ [HTTP, flex (plain + SSL)]
+ []
+ [[example_src example/http/server/flex/http_server_flex.cpp http_server_flex.cpp]]
+]]
-[endsect]
+These WebSocket servers echo back any message received, keeping the
+session open until the client disconnects.
+[table
+[[Description] [Source File] [Source File (using SSL)]]
+[
+ [WebSocket, synchronous]
+ [[example_src example/websocket/server/sync/websocket_server_sync.cpp websocket_server_sync.cpp]]
+ [[example_src example/websocket/server/sync-ssl/websocket_server_sync_ssl.cpp websocket_server_sync_ssl.cpp]]
+][
+ [WebSocket, asynchronous]
+ [[example_src example/websocket/server/async/websocket_server_async.cpp websocket_server_async.cpp]]
+ [[example_src example/websocket/server/async-ssl/websocket_server_async_ssl.cpp websocket_server_async_ssl.cpp]]
+][
+ [WebSocket, coroutine]
+ [[example_src example/websocket/server/coro/websocket_server_coro.cpp websocket_server_coro.cpp]]
+ [[example_src example/websocket/server/coro-ssl/websocket_server_coro_ssl.cpp websocket_server_coro_ssl.cpp]]
+][
+ [WebSocket, stackless coroutine]
+ [[example_src example/websocket/server/stackless/websocket_server_stackless.cpp websocket_server_stackless.cpp]]
+ [[example_src example/websocket/server/stackless-ssl/websocket_server_stackless_ssl.cpp websocket_server_stackless_ssl.cpp]]
+][
+ [WebSocket, fast (suited for benchmarks)]
+ [[example_src example/websocket/server/fast/websocket_server_fast.cpp websocket_server_fast.cpp]]
+ []
+]]
+[heading Advanced Servers]
-[section HTTP Server (Fast)]
+These servers offer both HTTP and WebSocket services on the same port,
+and illustrate the implementation of advanced features.
-This example implements a very simple HTTP server with
-some optimizations suitable for calculating benchmarks.
+[table
+[[Description] [Features] [Source File]]
+[
+ [Advanced]
+ [[itemized_list
+ [HTTP pipelining]
+ [Asynchronous timeouts]
+ [Dual protocols: HTTP and WebSocket]]]
+ [[example_src example/advanced/server/advanced_server.cpp advanced_server.cpp]]
+][
+ [Advanced, flex (plain + SSL)]
+ [[itemized_list
+ [HTTP pipelining]
+ [Asynchronous timeouts]
+ [Dual protocols: HTTP and WebSocket]
+ [Flexible ports; plain and SSL on the same port]]]
+ [[example_src example/advanced/server-flex/advanced_server_flex.cpp advanced_server_flex.cpp]]
+]]
-* [repo_file example/http-server-fast/fields_alloc.hpp]
-* [repo_file example/http-server-fast/http_server_fast.cpp]
+[heading Common Files]
-[endsect]
+Some of the examples use one or more shared header files, they are
+listed here along with a description of their use:
-
-
-[section HTTP Server (Small)]
-
-This example implements a very simple HTTP server
-suitable as a starting point on an embedded device.
-
-* [repo_file example/http-server-small/http_server_small.cpp]
-
-[endsect]
-
-
-
-[section HTTP Server (Threaded)]
-
-This example implements a very simple HTTP server using
-synchronous interfaces and using one thread per connection:
-
-* [repo_file example/http-server-threaded/http_server_threaded.cpp]
-
-[endsect]
-
-
-
-[section WebSocket Client (with SSL)]
-
-Establish a WebSocket connection over an encrypted TLS connection,
-send a message and receive the reply. Requires OpenSSL to build.
-
-* [repo_file example/websocket-client-ssl/websocket_client_ssl.cpp]
-
-[endsect]
-
-
-
-[section WebSocket Server (Asynchronous)]
-
-This program implements a WebSocket echo server using asynchronous
-interfaces and a configurable number of threads.
-
-* [repo_file example/websocket-server-async/websocket_server_async.cpp]
-
-[endsect]
+[table
+[[Source File] [Description]]
+[
+ [[repo_file example/common/detect_ssl.hpp]]
+ [
+ This contains the detect SSL algorithm including the
+ synchronous and asynchronous initiating functions, described
+ in the documentation. It is used by the "flex" servers which
+ support both plain and SSL sessions on the same port.
+ ]
+][
+ [[repo_file example/common/root_certificates.hpp]]
+ [
+ This contains the root SSL certificates used in the SSL client
+ examples. These certificates are used to verify the signatures
+ of SSL certificates presented by remote servers. They represent
+ a subset of the public keys usually installed as part of the
+ operating system or browser, so they may not identify every
+ possible server.
+ ]
+][
+ [[repo_file example/common/server_certificate.hpp]]
+ [
+ This file contains a self-signed SSL certificate used by the
+ SSL server examples. It has not been validated by a Certificate
+ Authority ("CA") so connecting to an example HTTP server using
+ a browser may generate security warnings.
+ ]
+][
+ [[repo_file example/common/ssl_stream.hpp]]
+ [
+ The `ssl_stream` is a replacement for `boost::asio::ssl::stream`
+ which supports construction from a moved-froms ocket and is also
+ itself move constructible.
+ ]
+][
+ [[repo_file example/common/write_msg.hpp]]
+ [
+ The function `async_write_msg` takes ownership of an HTTP message
+ using the move constructor, and sends it asynchronously. It is
+ used as a convenience for managing the lifetime of temporary
+ objects, as well as for implementing HTTP pipelining.
+ ]
+]]
@@ -143,7 +252,8 @@ in your program without modification
This program shows how to use Beast's network foundations to build a
composable asynchronous initiation function with associated composed
operation implementation. This is a complete, runnable version of
-the example described in the Core Foundations document section.
+the example described in
+[link beast.using_io.writing_composed_operations.echo Writing Composed Operations: Echo].
* [repo_file example/echo-op/echo_op.cpp]
@@ -151,46 +261,4 @@ the example described in the Core Foundations document section.
-[section Common Code]
-
-This code is reused between some of the examples. The header files
-stand alone can be directly included in your projects.
-
-* [repo_file example/common/detect_ssl.hpp]
-* [repo_file example/common/helpers.hpp]
-* [repo_file example/common/mime_types.hpp]
-* [repo_file example/common/rfc7231.hpp]
-* [repo_file example/common/ssl_stream.hpp]
-* [repo_file example/common/write_msg.hpp]
-
-[endsect]
-
-
-
-[section Server Framework]
-
-This is a complete program and framework of classes implementing
-a general purpose server that users may copy to use as the basis
-for writing their own servers. It serves both HTTP and WebSocket.
-
-* [repo_file example/server-framework/file_service.hpp]
-* [repo_file example/server-framework/framework.hpp]
-* [repo_file example/server-framework/http_async_port.hpp]
-* [repo_file example/server-framework/http_base.hpp]
-* [repo_file example/server-framework/http_sync_port.hpp]
-* [repo_file example/server-framework/https_ports.hpp]
-* [repo_file example/server-framework/main.cpp]
-* [repo_file example/server-framework/multi_port.hpp]
-* [repo_file example/server-framework/server.hpp]
-* [repo_file example/server-framework/service_list.hpp]
-* [repo_file example/server-framework/ssl_certificate.hpp]
-* [repo_file example/server-framework/ws_async_port.hpp]
-* [repo_file example/server-framework/ws_sync_port.hpp]
-* [repo_file example/server-framework/ws_upgrade_service.hpp]
-* [repo_file example/server-framework/wss_ports.hpp]
-
-[endsect]
-
-
-
[endsect]
diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt
index fde3a395..950c29db 100644
--- a/example/CMakeLists.txt
+++ b/example/CMakeLists.txt
@@ -7,17 +7,8 @@
# Official repository: https://github.com/boostorg/beast
#
-add_subdirectory (echo-op)
-add_subdirectory (http-client)
-add_subdirectory (http-crawl)
-add_subdirectory (http-server-fast)
-add_subdirectory (http-server-small)
-add_subdirectory (http-server-threaded)
-add_subdirectory (server-framework)
-add_subdirectory (websocket-client)
-add_subdirectory (websocket-server-async)
+add_subdirectory (advanced)
+add_subdirectory (http)
+add_subdirectory (websocket)
-if (OPENSSL_FOUND)
- add_subdirectory (http-client-ssl)
- add_subdirectory (websocket-client-ssl)
-endif()
+add_subdirectory (echo-op)
diff --git a/example/Jamfile b/example/Jamfile
index e7059521..cccbfe4b 100644
--- a/example/Jamfile
+++ b/example/Jamfile
@@ -7,16 +7,9 @@
# Official repository: https://github.com/boostorg/beast
#
-build-project echo-op ;
-build-project http-client ;
-build-project http-crawl ;
-build-project http-server-fast ;
-build-project http-server-small ;
-build-project http-server-threaded ;
-build-project server-framework ;
-build-project websocket-client ;
-build-project websocket-server-async ;
+build-project advanced ;
+build-project http ;
+build-project websocket ;
-# VFALCO How do I make this work on Windows and if OpenSSL is not available?
-#build-project ssl-http-client ;
-#build-project ssl-websocket-client ;
+# legacy
+build-project echo-op ;
diff --git a/example/advanced/CMakeLists.txt b/example/advanced/CMakeLists.txt
new file mode 100644
index 00000000..af83e043
--- /dev/null
+++ b/example/advanced/CMakeLists.txt
@@ -0,0 +1,11 @@
+#
+# Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+#
+# Distributed under the Boost Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#
+# Official repository: https://github.com/boostorg/beast
+#
+
+add_subdirectory (server)
+add_subdirectory (server-flex)
diff --git a/example/advanced/Jamfile b/example/advanced/Jamfile
new file mode 100644
index 00000000..2524238b
--- /dev/null
+++ b/example/advanced/Jamfile
@@ -0,0 +1,13 @@
+#
+# Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+#
+# Distributed under the Boost Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#
+# Official repository: https://github.com/boostorg/beast
+#
+
+build-project server ;
+
+# VFALCO How do I make this work on Windows and if OpenSSL is not available?
+#build-project server-flex ;
diff --git a/example/advanced/server-flex/CMakeLists.txt b/example/advanced/server-flex/CMakeLists.txt
new file mode 100644
index 00000000..fca97db8
--- /dev/null
+++ b/example/advanced/server-flex/CMakeLists.txt
@@ -0,0 +1,26 @@
+#
+# Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+#
+# Distributed under the Boost Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#
+# Official repository: https://github.com/boostorg/beast
+#
+
+GroupSources(include/boost/beast beast)
+GroupSources(example/common common)
+GroupSources(example/advanced/server-flex "/")
+
+add_executable (advanced-server-flex
+ ${BOOST_BEAST_INCLUDES}
+ ${PROJECT_SOURCE_DIR}/example/common/detect_ssl.hpp
+ ${PROJECT_SOURCE_DIR}/example/common/server_certificate.hpp
+ ${PROJECT_SOURCE_DIR}/example/common/ssl_stream.hpp
+ ${PROJECT_SOURCE_DIR}/example/common/write_msg.hpp
+ Jamfile
+ advanced_server_flex.cpp
+)
+
+target_link_libraries (advanced-server-flex
+ ${OPENSSL_LIBRARIES}
+ )
diff --git a/example/advanced/server-flex/Jamfile b/example/advanced/server-flex/Jamfile
new file mode 100644
index 00000000..dfa18256
--- /dev/null
+++ b/example/advanced/server-flex/Jamfile
@@ -0,0 +1,21 @@
+#
+# Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+#
+# Distributed under the Boost Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#
+# Official repository: https://github.com/boostorg/beast
+#
+
+project
+ : requirements
+ ssl
+ crypto
+ ;
+
+exe advanced-server-flex :
+ advanced_server_flex.cpp
+ :
+ coverage:no
+ ubasan:no
+ ;
diff --git a/example/advanced/server-flex/advanced_server_flex.cpp b/example/advanced/server-flex/advanced_server_flex.cpp
new file mode 100644
index 00000000..fee47c78
--- /dev/null
+++ b/example/advanced/server-flex/advanced_server_flex.cpp
@@ -0,0 +1,1171 @@
+//
+// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+//------------------------------------------------------------------------------
+//
+// Example: Advanced server, flex (plain + SSL)
+//
+//------------------------------------------------------------------------------
+
+#include "example/common/detect_ssl.hpp"
+#include "example/common/server_certificate.hpp"
+#include "example/common/ssl_stream.hpp"
+#include "example/common/write_msg.hpp"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+using tcp = boost::asio::ip::tcp; // from
+namespace ssl = boost::asio::ssl; // from
+namespace http = boost::beast::http; // from
+namespace websocket = boost::beast::websocket; // from
+
+// Return a reasonable mime type based on the extension of a file.
+boost::beast::string_view
+mime_type(boost::beast::string_view path)
+{
+ using boost::beast::iequals;
+ auto const ext = [&path]
+ {
+ auto const pos = path.rfind(".");
+ if(pos == boost::beast::string_view::npos)
+ return boost::beast::string_view{};
+ return path.substr(pos);
+ }();
+ if(iequals(ext, ".htm")) return "text/html";
+ if(iequals(ext, ".html")) return "text/html";
+ if(iequals(ext, ".php")) return "text/html";
+ if(iequals(ext, ".css")) return "text/css";
+ if(iequals(ext, ".txt")) return "text/plain";
+ if(iequals(ext, ".js")) return "application/javascript";
+ if(iequals(ext, ".json")) return "application/json";
+ if(iequals(ext, ".xml")) return "application/xml";
+ if(iequals(ext, ".swf")) return "application/x-shockwave-flash";
+ if(iequals(ext, ".flv")) return "video/x-flv";
+ if(iequals(ext, ".png")) return "image/png";
+ if(iequals(ext, ".jpe")) return "image/jpeg";
+ if(iequals(ext, ".jpeg")) return "image/jpeg";
+ if(iequals(ext, ".jpg")) return "image/jpeg";
+ if(iequals(ext, ".gif")) return "image/gif";
+ if(iequals(ext, ".bmp")) return "image/bmp";
+ if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon";
+ if(iequals(ext, ".tiff")) return "image/tiff";
+ if(iequals(ext, ".tif")) return "image/tiff";
+ if(iequals(ext, ".svg")) return "image/svg+xml";
+ if(iequals(ext, ".svgz")) return "image/svg+xml";
+ return "application/text";
+}
+
+// Append an HTTP rel-path to a local filesystem path.
+// The returned path is normalized for the platform.
+std::string
+path_cat(
+ boost::beast::string_view base,
+ boost::beast::string_view path)
+{
+ if(base.empty())
+ return path.to_string();
+ std::string result = base.to_string();
+#if BOOST_MSVC
+ char constexpr path_separator = '\\';
+ if(result.back() == path_separator)
+ result.resize(result.size() - 1);
+ result.append(path.data(), path.size());
+ for(auto& c : result)
+ if(c == '/')
+ c = path_separator;
+#else
+ char constexpr path_separator = '/';
+ if(result.back() == path_separator)
+ result.resize(result.size() - 1);
+ result.append(path.data(), path.size());
+#endif
+ return result;
+}
+
+// This function produces an HTTP response for the given
+// request. The type of the response object depends on the
+// contents of the request, so the interface requires the
+// caller to pass a generic lambda for receiving the response.
+template<
+ class Body, class Allocator,
+ class Send>
+void
+handle_request(
+ boost::beast::string_view doc_root,
+ http::request>&& req,
+ Send&& send)
+{
+ // Returns a bad request response
+ auto const bad_request =
+ [&req](boost::beast::string_view why)
+ {
+ http::response res{http::status::bad_request, req.version};
+ res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
+ res.set(http::field::content_type, "text/html");
+ res.keep_alive(req.keep_alive());
+ res.body = why.to_string();
+ res.prepare_payload();
+ return res;
+ };
+
+ // Returns a not found response
+ auto const not_found =
+ [&req](boost::beast::string_view target)
+ {
+ http::response res{http::status::not_found, req.version};
+ res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
+ res.set(http::field::content_type, "text/html");
+ res.keep_alive(req.keep_alive());
+ res.body = "The resource '" + target.to_string() + "' was not found.";
+ res.prepare_payload();
+ return res;
+ };
+
+ // Returns a server error response
+ auto const server_error =
+ [&req](boost::beast::string_view what)
+ {
+ http::response res{http::status::internal_server_error, req.version};
+ res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
+ res.set(http::field::content_type, "text/html");
+ res.keep_alive(req.keep_alive());
+ res.body = "An error occurred: '" + what.to_string() + "'";
+ res.prepare_payload();
+ return res;
+ };
+
+ // Make sure we can handle the method
+ if( req.method() != http::verb::get &&
+ req.method() != http::verb::head)
+ return send(bad_request("Unknown HTTP-method"));
+
+ // Request path must be absolute and not contain "..".
+ if( req.target().empty() ||
+ req.target()[0] != '/' ||
+ req.target().find("..") != boost::beast::string_view::npos)
+ return send(bad_request("Illegal request-target"));
+
+ // Build the path to the requested file
+ std::string path = path_cat(doc_root, req.target());
+ if(req.target().back() == '/')
+ path.append("index.html");
+
+ // Attempt to open the file
+ boost::beast::error_code ec;
+ http::file_body::value_type body;
+ body.open(path.c_str(), boost::beast::file_mode::scan, ec);
+
+ // Handle the case where the file doesn't exist
+ if(ec == boost::system::errc::no_such_file_or_directory)
+ return send(not_found(req.target()));
+
+ // Handle an unknown error
+ if(ec)
+ return send(server_error(ec.message()));
+
+ // Respond to HEAD request
+ if(req.method() == http::verb::head)
+ {
+ http::response res{http::status::ok, req.version};
+ res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
+ res.set(http::field::content_type, mime_type(path));
+ res.content_length(body.size());
+ res.keep_alive(req.keep_alive());
+ return send(std::move(res));
+ }
+
+ // Respond to HEAD request
+ http::response res{
+ std::piecewise_construct,
+ std::make_tuple(std::move(body)),
+ std::make_tuple(http::status::ok, req.version)};
+ res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
+ res.set(http::field::content_type, mime_type(path));
+ res.content_length(body.size());
+ res.keep_alive(req.keep_alive());
+ return send(std::move(res));
+}
+
+//------------------------------------------------------------------------------
+
+// Report a failure
+void
+fail(boost::system::error_code ec, char const* what)
+{
+ std::cerr << what << ": " << ec.message() << "\n";
+}
+
+//------------------------------------------------------------------------------
+
+// Echoes back all received WebSocket messages.
+// This uses the Curiously Recurring Template Pattern so that
+// the same code works with both SSL streams and regular sockets.
+template
+class websocket_session
+{
+ // Access the derived class, this is part of
+ // the Curiously Recurring Template Pattern idiom.
+ Derived&
+ derived()
+ {
+ return static_cast(*this);
+ }
+
+ boost::beast::multi_buffer buffer_;
+
+protected:
+ boost::asio::io_service::strand strand_;
+ boost::asio::steady_timer timer_;
+
+public:
+ // Construct the session
+ explicit
+ websocket_session(boost::asio::io_service& ios)
+ : strand_(ios)
+ , timer_(ios,
+ (std::chrono::steady_clock::time_point::max)())
+ {
+ }
+
+ // Start the asynchronous operation
+ template
+ void
+ do_accept(http::request> req)
+ {
+ // Set the timer
+ timer_.expires_from_now(std::chrono::seconds(15));
+
+ // Accept the websocket handshake
+ derived().ws().async_accept(
+ req,
+ strand_.wrap(std::bind(
+ &websocket_session::on_accept,
+ derived().shared_from_this(),
+ std::placeholders::_1)));
+ }
+
+ // Called when the timer expires.
+ void
+ on_timer(boost::system::error_code ec)
+ {
+ if(ec && ec != boost::asio::error::operation_aborted)
+ return fail(ec, "timer");
+
+ // Verify that the timer really expired since the deadline may have moved.
+ if(timer_.expires_at() <= std::chrono::steady_clock::now())
+ derived().do_timeout();
+
+ // Wait on the timer
+ timer_.async_wait(
+ strand_.wrap(std::bind(
+ &websocket_session::on_timer,
+ derived().shared_from_this(),
+ std::placeholders::_1)));
+ }
+
+ void
+ on_accept(boost::system::error_code ec)
+ {
+ // Happens when the timer closes the socket
+ if(ec == boost::asio::error::operation_aborted)
+ return;
+
+ if(ec)
+ return fail(ec, "accept");
+
+ // Read a message
+ do_read();
+ }
+
+ void
+ do_read()
+ {
+ // Set the timer
+ timer_.expires_from_now(std::chrono::seconds(15));
+
+ // Read a message into our buffer
+ derived().ws().async_read(
+ buffer_,
+ strand_.wrap(std::bind(
+ &websocket_session::on_read,
+ derived().shared_from_this(),
+ std::placeholders::_1)));
+ }
+
+ void
+ on_read(boost::system::error_code ec)
+ {
+ // Happens when the timer closes the socket
+ if(ec == boost::asio::error::operation_aborted)
+ return;
+
+ // This indicates that the websocket_session was closed
+ if(ec == websocket::error::closed)
+ return;
+
+ if(ec)
+ fail(ec, "read");
+
+ // Echo the message
+ derived().ws().text(derived().ws().got_text());
+ derived().ws().async_write(
+ buffer_.data(),
+ strand_.wrap(std::bind(
+ &websocket_session::on_write,
+ derived().shared_from_this(),
+ std::placeholders::_1)));
+ }
+
+ void
+ on_write(boost::system::error_code ec)
+ {
+ // Happens when the timer closes the socket
+ if(ec == boost::asio::error::operation_aborted)
+ return;
+
+ if(ec)
+ return fail(ec, "write");
+
+ // Clear the buffer
+ buffer_.consume(buffer_.size());
+
+ // Do another read
+ do_read();
+ }
+};
+
+// Handles a plain WebSocket connection
+class plain_websocket_session
+ : public websocket_session
+ , public std::enable_shared_from_this
+{
+ websocket::stream ws_;
+ bool close_ = false;
+
+public:
+ // Create the session
+ explicit
+ plain_websocket_session(tcp::socket socket)
+ : websocket_session(
+ socket.get_io_service())
+ , ws_(std::move(socket))
+ {
+ }
+
+ // Called by the base class
+ websocket::stream&
+ ws()
+ {
+ return ws_;
+ }
+
+ // Start the asynchronous operation
+ template
+ void
+ run(http::request> req)
+ {
+ // Run the timer. The timer is operated
+ // continuously, this simplifies the code.
+ on_timer({});
+
+ // Accept the WebSocket upgrade request
+ do_accept(std::move(req));
+ }
+
+ void
+ do_timeout()
+ {
+ // This is so the close can have a timeout
+ if(close_)
+ return;
+ close_ = true;
+
+ // Set the timer
+ timer_.expires_from_now(std::chrono::seconds(15));
+
+ // Close the WebSocket Connection
+ ws_.async_close(
+ websocket::close_code::normal,
+ strand_.wrap(std::bind(
+ &plain_websocket_session::on_close,
+ shared_from_this(),
+ std::placeholders::_1)));
+ }
+
+ void
+ on_close(boost::system::error_code ec)
+ {
+ // Happens when close times out
+ if(ec == boost::asio::error::operation_aborted)
+ return;
+
+ if(ec)
+ return fail(ec, "close");
+
+ // At this point the connection is gracefully closed
+ }
+};
+
+// Handles an SSL WebSocket connection
+class ssl_websocket_session
+ : public websocket_session
+ , public std::enable_shared_from_this
+{
+ websocket::stream> ws_;
+ boost::asio::io_service::strand strand_;
+ bool eof_ = false;
+
+public:
+ // Create the http_session
+ explicit
+ ssl_websocket_session(ssl_stream stream)
+ : websocket_session(
+ stream.get_io_service())
+ , ws_(std::move(stream))
+ , strand_(ws_.get_io_service())
+ {
+ }
+
+ // Called by the base class
+ websocket::stream>&
+ ws()
+ {
+ return ws_;
+ }
+
+ // Start the asynchronous operation
+ template
+ void
+ run(http::request> req)
+ {
+ // Run the timer. The timer is operated
+ // continuously, this simplifies the code.
+ on_timer({});
+
+ // Accept the WebSocket upgrade request
+ do_accept(std::move(req));
+ }
+
+ void
+ do_eof()
+ {
+ eof_ = true;
+
+ // Set the timer
+ timer_.expires_from_now(std::chrono::seconds(15));
+
+ // Perform the SSL shutdown
+ ws_.next_layer().async_shutdown(
+ strand_.wrap(std::bind(
+ &ssl_websocket_session::on_shutdown,
+ shared_from_this(),
+ std::placeholders::_1)));
+ }
+
+ void
+ on_shutdown(boost::system::error_code ec)
+ {
+ // Happens when the shutdown times out
+ if(ec == boost::asio::error::operation_aborted)
+ return;
+
+ if(ec)
+ return fail(ec, "shutdown");
+
+ // At this point the connection is closed gracefully
+ }
+
+ void
+ do_timeout()
+ {
+ // If this is true it means we timed out performing the shutdown
+ if(eof_)
+ return;
+
+ // Start the timer again
+ timer_.expires_at(
+ (std::chrono::steady_clock::time_point::max)());
+ on_timer({});
+ do_eof();
+ }
+};
+
+template
+void
+make_websocket_session(
+ tcp::socket socket,
+ http::request> req)
+{
+ std::make_shared(
+ std::move(socket))->run(std::move(req));
+}
+
+template
+void
+make_websocket_session(
+ ssl_stream stream,
+ http::request> req)
+{
+ std::make_shared(
+ std::move(stream))->run(std::move(req));
+}
+
+//------------------------------------------------------------------------------
+
+// Handles an HTTP server connection.
+// This uses the Curiously Recurring Template Pattern so that
+// the same code works with both SSL streams and regular sockets.
+template
+class http_session
+{
+ // Access the derived class, this is part of
+ // the Curiously Recurring Template Pattern idiom.
+ Derived&
+ derived()
+ {
+ return static_cast(*this);
+ }
+
+ // This queue is used for HTTP pipelining.
+ class queue
+ {
+ enum
+ {
+ // Maximum number of responses we will queue
+ limit = 8
+ };
+
+ // The type-erased, saved work item
+ struct work
+ {
+ virtual ~work() = default;
+ virtual void operator()() = 0;
+ };
+
+ bool busy_ = false; // true if a write is in progress
+ http_session& self_;
+ std::vector> items_;
+
+ public:
+ explicit
+ queue(http_session& self)
+ : self_(self)
+ {
+ static_assert(limit > 0, "queue limit must be positive");
+ items_.reserve(limit);
+ }
+
+ // Returns `true` if we have reached the queue limit
+ bool
+ is_full() const
+ {
+ return items_.size() + (busy_ ? 1 : 0) >= limit;
+ }
+
+ // Called when a message finishes sending
+ // Returns `true` if the caller should initiate a read
+ bool
+ on_write()
+ {
+ BOOST_ASSERT(busy_);
+ auto const do_read = items_.size() + (busy_ ? 1 : 0) >= limit;
+ if(! items_.empty())
+ {
+ (*items_.back())();
+ items_.erase(items_.begin());
+ }
+ else
+ {
+ busy_ = false;
+ }
+ return do_read;
+ }
+
+ // Called by the HTTP handler to send a response.
+ template
+ void
+ operator()(http::message&& msg)
+ {
+ // See if a write is in progress
+ if(! busy_)
+ {
+ // No write in progress so start one
+ busy_ = true;
+
+ // This function takes ownership of the message by moving
+ // it into a temporary buffer, otherwise we would have
+ // to manage the lifetime of the message and serializer.
+ return async_write_msg(
+ self_.derived().stream(),
+ std::move(msg),
+ self_.strand_.wrap(std::bind(
+ &http_session::on_write,
+ self_.derived().shared_from_this(),
+ std::placeholders::_1)));
+ }
+
+ // This holds a work item
+ struct work_impl : work
+ {
+ http_session& self_;
+ http::message msg_;
+
+ work_impl(
+ http_session& self,
+ http::message&& msg)
+ : self_(self)
+ , msg_(std::move(msg))
+ {
+ }
+
+ void
+ operator()()
+ {
+ async_write_msg(
+ self_.derived().stream(),
+ std::move(msg_),
+ self_.strand_.wrap(std::bind(
+ &http_session::on_write,
+ self_.derived().shared_from_this(),
+ std::placeholders::_1)));
+ }
+ };
+
+ // A write is in progress, so allocate storage to
+ // save this work item so we can invoke it later.
+ items_.emplace_back(new work_impl(self_, std::move(msg)));
+ }
+ };
+
+ std::string const& doc_root_;
+ http::request req_;
+ queue queue_;
+
+protected:
+ boost::asio::steady_timer timer_;
+ boost::asio::io_service::strand strand_;
+ boost::beast::flat_buffer buffer_;
+
+public:
+ // Construct the session
+ http_session(
+ boost::asio::io_service& ios,
+ boost::beast::flat_buffer buffer,
+ std::string const& doc_root)
+ : doc_root_(doc_root)
+ , queue_(*this)
+ , timer_(ios,
+ (std::chrono::steady_clock::time_point::max)())
+ , strand_(ios)
+ , buffer_(std::move(buffer))
+ {
+ }
+
+ void
+ do_read()
+ {
+ // Set the timer
+ timer_.expires_from_now(std::chrono::seconds(15));
+
+ // Read a request
+ http::async_read(
+ derived().stream(),
+ buffer_,
+ req_,
+ strand_.wrap(std::bind(
+ &http_session::on_read,
+ derived().shared_from_this(),
+ std::placeholders::_1)));
+ }
+
+ // Called when the timer expires.
+ void
+ on_timer(boost::system::error_code ec)
+ {
+ if(ec && ec != boost::asio::error::operation_aborted)
+ return fail(ec, "timer");
+
+ // Verify that the timer really expired since the deadline may have moved.
+ if(timer_.expires_at() <= std::chrono::steady_clock::now())
+ return derived().do_timeout();
+
+ // Wait on the timer
+ timer_.async_wait(
+ strand_.wrap(std::bind(
+ &http_session::on_timer,
+ derived().shared_from_this(),
+ std::placeholders::_1)));
+ }
+
+ void
+ on_read(boost::system::error_code ec)
+ {
+ // Happens when the timer closes the socket
+ if(ec == boost::asio::error::operation_aborted)
+ return;
+
+ // This means they closed the connection
+ if(ec == http::error::end_of_stream)
+ return derived().do_eof();
+
+ if(ec)
+ return fail(ec, "read");
+
+ // See if it is a WebSocket Upgrade
+ if(websocket::is_upgrade(req_))
+ {
+ // Transfer the stream to a new WebSocket session
+ return make_websocket_session(
+ derived().release_stream(),
+ std::move(req_));
+ }
+
+ // Send the response
+ handle_request(doc_root_, std::move(req_), queue_);
+
+ // If we aren't at the queue limit, try to pipeline another request
+ if(! queue_.is_full())
+ do_read();
+ }
+
+ void
+ on_write(boost::system::error_code ec)
+ {
+ // Happens when the timer closes the socket
+ if(ec == boost::asio::error::operation_aborted)
+ return;
+
+ if(ec == http::error::end_of_stream)
+ {
+ // This means we should close the connection, usually because
+ // the response indicated the "Connection: close" semantic.
+ return derived().do_eof();
+ }
+
+ if(ec)
+ return fail(ec, "write");
+
+ // Inform the queue that a write completed
+ if(queue_.on_write())
+ {
+ // Read another request
+ do_read();
+ }
+ }
+};
+
+// Handles a plain HTTP connection
+class plain_http_session
+ : public http_session
+ , public std::enable_shared_from_this
+{
+ tcp::socket socket_;
+ boost::asio::io_service::strand strand_;
+
+public:
+ // Create the http_session
+ plain_http_session(
+ tcp::socket socket,
+ boost::beast::flat_buffer buffer,
+ std::string const& doc_root)
+ : http_session(
+ socket.get_io_service(),
+ std::move(buffer),
+ doc_root)
+ , socket_(std::move(socket))
+ , strand_(socket_.get_io_service())
+ {
+ }
+
+ // Called by the base class
+ tcp::socket&
+ stream()
+ {
+ return socket_;
+ }
+
+ // Called by the base class
+ tcp::socket
+ release_stream()
+ {
+ return std::move(socket_);
+ }
+
+ // Start the asynchronous operation
+ void
+ run()
+ {
+ // Run the timer. The timer is operated
+ // continuously, this simplifies the code.
+ on_timer({});
+
+ do_read();
+ }
+
+ void
+ do_eof()
+ {
+ // Send a TCP shutdown
+ boost::system::error_code ec;
+ socket_.shutdown(tcp::socket::shutdown_send, ec);
+
+ // At this point the connection is closed gracefully
+ }
+
+ void
+ do_timeout()
+ {
+ // Closing the socket cancels all outstanding operations. They
+ // will complete with boost::asio::error::operation_aborted
+ boost::system::error_code ec;
+ socket_.shutdown(tcp::socket::shutdown_both, ec);
+ socket_.close(ec);
+ }
+};
+
+// Handles an SSL HTTP connection
+class ssl_http_session
+ : public http_session
+ , public std::enable_shared_from_this
+{
+ ssl_stream stream_;
+ boost::asio::io_service::strand strand_;
+ bool eof_ = false;
+
+public:
+ // Create the http_session
+ ssl_http_session(
+ tcp::socket socket,
+ ssl::context& ctx,
+ boost::beast::flat_buffer buffer,
+ std::string const& doc_root)
+ : http_session(
+ socket.get_io_service(),
+ std::move(buffer),
+ doc_root)
+ , stream_(std::move(socket), ctx)
+ , strand_(stream_.get_io_service())
+ {
+ }
+
+ // Called by the base class
+ ssl_stream&
+ stream()
+ {
+ return stream_;
+ }
+
+ // Called by the base class
+ ssl_stream
+ release_stream()
+ {
+ return std::move(stream_);
+ }
+
+ // Start the asynchronous operation
+ void
+ run()
+ {
+ // Run the timer. The timer is operated
+ // continuously, this simplifies the code.
+ on_timer({});
+
+ // Set the timer
+ timer_.expires_from_now(std::chrono::seconds(15));
+
+ // Perform the SSL handshake
+ // Note, this is the buffered version of the handshake.
+ stream_.async_handshake(
+ ssl::stream_base::server,
+ buffer_.data(),
+ strand_.wrap(std::bind(
+ &ssl_http_session::on_handshake,
+ shared_from_this(),
+ std::placeholders::_1,
+ std::placeholders::_2)));
+ }
+ void
+ on_handshake(
+ boost::system::error_code ec,
+ std::size_t bytes_used)
+ {
+ // Happens when the handshake times out
+ if(ec == boost::asio::error::operation_aborted)
+ return;
+
+ if(ec)
+ return fail(ec, "handshake");
+
+ // Consume the portion of the buffer used by the handshake
+ buffer_.consume(bytes_used);
+
+ do_read();
+ }
+
+ void
+ do_eof()
+ {
+ eof_ = true;
+
+ // Set the timer
+ timer_.expires_from_now(std::chrono::seconds(15));
+
+ // Perform the SSL shutdown
+ stream_.async_shutdown(
+ strand_.wrap(std::bind(
+ &ssl_http_session::on_shutdown,
+ shared_from_this(),
+ std::placeholders::_1)));
+ }
+
+ void
+ on_shutdown(boost::system::error_code ec)
+ {
+ // Happens when the shutdown times out
+ if(ec == boost::asio::error::operation_aborted)
+ return;
+
+ if(ec)
+ return fail(ec, "shutdown");
+
+ // At this point the connection is closed gracefully
+ }
+
+ void
+ do_timeout()
+ {
+ // If this is true it means we timed out performing the shutdown
+ if(eof_)
+ return;
+
+ // Start the timer again
+ timer_.expires_at(
+ (std::chrono::steady_clock::time_point::max)());
+ on_timer({});
+ do_eof();
+ }
+};
+
+//------------------------------------------------------------------------------
+
+// Detects SSL handshakes
+class detect_session : public std::enable_shared_from_this
+{
+ tcp::socket socket_;
+ ssl::context& ctx_;
+ boost::asio::io_service::strand strand_;
+ std::string const& doc_root_;
+ boost::beast::flat_buffer buffer_;
+
+public:
+ explicit
+ detect_session(
+ tcp::socket socket,
+ ssl::context& ctx,
+ std::string const& doc_root)
+ : socket_(std::move(socket))
+ , ctx_(ctx)
+ , strand_(socket_.get_io_service())
+ , doc_root_(doc_root)
+ {
+ }
+
+ // Launch the detector
+ void
+ run()
+ {
+ async_detect_ssl(
+ socket_,
+ buffer_,
+ strand_.wrap(std::bind(
+ &detect_session::on_detect,
+ shared_from_this(),
+ std::placeholders::_1,
+ std::placeholders::_2)));
+
+ }
+
+ void
+ on_detect(boost::system::error_code ec, boost::tribool result)
+ {
+ if(ec)
+ return fail(ec, "detect");
+
+ if(result)
+ {
+ // Launch SSL session
+ std::make_shared(
+ std::move(socket_),
+ ctx_,
+ std::move(buffer_),
+ doc_root_)->run();
+ return;
+ }
+
+ // Launch plain session
+ std::make_shared(
+ std::move(socket_),
+ std::move(buffer_),
+ doc_root_)->run();
+ }
+};
+
+// Accepts incoming connections and launches the sessions
+class listener : public std::enable_shared_from_this
+{
+ ssl::context& ctx_;
+ boost::asio::io_service::strand strand_;
+ tcp::acceptor acceptor_;
+ tcp::socket socket_;
+ std::string const& doc_root_;
+
+public:
+ listener(
+ boost::asio::io_service& ios,
+ ssl::context& ctx,
+ tcp::endpoint endpoint,
+ std::string const& doc_root)
+ : ctx_(ctx)
+ , strand_(ios)
+ , acceptor_(ios)
+ , socket_(ios)
+ , doc_root_(doc_root)
+ {
+ boost::system::error_code ec;
+
+ // Open the acceptor
+ acceptor_.open(endpoint.protocol(), ec);
+ if(ec)
+ {
+ fail(ec, "open");
+ return;
+ }
+
+ // Bind to the server address
+ acceptor_.bind(endpoint, ec);
+ if(ec)
+ {
+ fail(ec, "bind");
+ return;
+ }
+
+ // Start listening for connections
+ acceptor_.listen(
+ boost::asio::socket_base::max_connections, ec);
+ if(ec)
+ {
+ fail(ec, "listen");
+ return;
+ }
+ }
+
+ // Start accepting incoming connections
+ void
+ run()
+ {
+ if(! acceptor_.is_open())
+ return;
+ do_accept();
+ }
+
+ void
+ do_accept()
+ {
+ acceptor_.async_accept(
+ socket_,
+ std::bind(
+ &listener::on_accept,
+ shared_from_this(),
+ std::placeholders::_1));
+ }
+
+ void
+ on_accept(boost::system::error_code ec)
+ {
+ if(ec)
+ {
+ fail(ec, "accept");
+ }
+ else
+ {
+ // Create the detector http_session and run it
+ std::make_shared(
+ std::move(socket_),
+ ctx_,
+ doc_root_)->run();
+ }
+
+ // Accept another connection
+ do_accept();
+ }
+};
+
+//------------------------------------------------------------------------------
+
+int main(int argc, char* argv[])
+{
+ // Check command line arguments.
+ if (argc != 5)
+ {
+ std::cerr <<
+ "Usage: advanced-server-flex \n" <<
+ "Example:\n" <<
+ " advanced-server-flex 0.0.0.0 8080 . 1\n";
+ return EXIT_FAILURE;
+ }
+ auto const address = boost::asio::ip::address::from_string(argv[1]);
+ auto const port = static_cast(std::atoi(argv[2]));
+ std::string const doc_root = argv[3];
+ auto const threads = std::max(1, std::atoi(argv[3]));
+
+ // The io_service is required for all I/O
+ boost::asio::io_service ios{threads};
+
+ // The SSL context is required, and holds certificates
+ ssl::context ctx{ssl::context::sslv23};
+
+ // This holds the self-signed certificate used by the server
+ load_server_certificate(ctx);
+
+ // Create and launch a listening port
+ std::make_shared(
+ ios,
+ ctx,
+ tcp::endpoint{address, port},
+ doc_root)->run();
+
+ // Run the I/O service on the requested number of threads
+ std::vector v;
+ v.reserve(threads - 1);
+ for(auto i = threads - 1; i > 0; --i)
+ v.emplace_back(
+ [&ios]
+ {
+ ios.run();
+ });
+ ios.run();
+
+ return EXIT_SUCCESS;
+}
diff --git a/example/http-server-threaded/CMakeLists.txt b/example/advanced/server/CMakeLists.txt
similarity index 72%
rename from example/http-server-threaded/CMakeLists.txt
rename to example/advanced/server/CMakeLists.txt
index 0055c40d..315d6d5e 100644
--- a/example/http-server-threaded/CMakeLists.txt
+++ b/example/advanced/server/CMakeLists.txt
@@ -9,11 +9,11 @@
GroupSources(include/boost/beast beast)
GroupSources(example/common common)
-GroupSources(example/http-server-threaded "/")
+GroupSources(example/advanced/server "/")
-add_executable (http-server-threaded
+add_executable (advanced-server
${BOOST_BEAST_INCLUDES}
- ${COMMON_INCLUDES}
+ ${PROJECT_SOURCE_DIR}/example/common/write_msg.hpp
Jamfile
- http_server_threaded.cpp
+ advanced_server.cpp
)
diff --git a/example/server-framework/Jamfile b/example/advanced/server/Jamfile
similarity index 88%
rename from example/server-framework/Jamfile
rename to example/advanced/server/Jamfile
index d90044cd..5f9ceecd 100644
--- a/example/server-framework/Jamfile
+++ b/example/advanced/server/Jamfile
@@ -7,8 +7,8 @@
# Official repository: https://github.com/boostorg/beast
#
-exe server-framework :
- main.cpp
+exe advanced-server :
+ advanced_server.cpp
:
coverage:no
ubasan:no
diff --git a/example/advanced/server/advanced_server.cpp b/example/advanced/server/advanced_server.cpp
new file mode 100644
index 00000000..fd199d85
--- /dev/null
+++ b/example/advanced/server/advanced_server.cpp
@@ -0,0 +1,732 @@
+//
+// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+//------------------------------------------------------------------------------
+//
+// Example: Advanced server
+//
+//------------------------------------------------------------------------------
+
+#include "example/common/write_msg.hpp"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+using tcp = boost::asio::ip::tcp; // from
+namespace http = boost::beast::http; // from
+namespace websocket = boost::beast::websocket; // from
+
+// Return a reasonable mime type based on the extension of a file.
+boost::beast::string_view
+mime_type(boost::beast::string_view path)
+{
+ using boost::beast::iequals;
+ auto const ext = [&path]
+ {
+ auto const pos = path.rfind(".");
+ if(pos == boost::beast::string_view::npos)
+ return boost::beast::string_view{};
+ return path.substr(pos);
+ }();
+ if(iequals(ext, ".htm")) return "text/html";
+ if(iequals(ext, ".html")) return "text/html";
+ if(iequals(ext, ".php")) return "text/html";
+ if(iequals(ext, ".css")) return "text/css";
+ if(iequals(ext, ".txt")) return "text/plain";
+ if(iequals(ext, ".js")) return "application/javascript";
+ if(iequals(ext, ".json")) return "application/json";
+ if(iequals(ext, ".xml")) return "application/xml";
+ if(iequals(ext, ".swf")) return "application/x-shockwave-flash";
+ if(iequals(ext, ".flv")) return "video/x-flv";
+ if(iequals(ext, ".png")) return "image/png";
+ if(iequals(ext, ".jpe")) return "image/jpeg";
+ if(iequals(ext, ".jpeg")) return "image/jpeg";
+ if(iequals(ext, ".jpg")) return "image/jpeg";
+ if(iequals(ext, ".gif")) return "image/gif";
+ if(iequals(ext, ".bmp")) return "image/bmp";
+ if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon";
+ if(iequals(ext, ".tiff")) return "image/tiff";
+ if(iequals(ext, ".tif")) return "image/tiff";
+ if(iequals(ext, ".svg")) return "image/svg+xml";
+ if(iequals(ext, ".svgz")) return "image/svg+xml";
+ return "application/text";
+}
+
+// Append an HTTP rel-path to a local filesystem path.
+// The returned path is normalized for the platform.
+std::string
+path_cat(
+ boost::beast::string_view base,
+ boost::beast::string_view path)
+{
+ if(base.empty())
+ return path.to_string();
+ std::string result = base.to_string();
+#if BOOST_MSVC
+ char constexpr path_separator = '\\';
+ if(result.back() == path_separator)
+ result.resize(result.size() - 1);
+ result.append(path.data(), path.size());
+ for(auto& c : result)
+ if(c == '/')
+ c = path_separator;
+#else
+ char constexpr path_separator = '/';
+ if(result.back() == path_separator)
+ result.resize(result.size() - 1);
+ result.append(path.data(), path.size());
+#endif
+ return result;
+}
+
+// This function produces an HTTP response for the given
+// request. The type of the response object depends on the
+// contents of the request, so the interface requires the
+// caller to pass a generic lambda for receiving the response.
+template<
+ class Body, class Allocator,
+ class Send>
+void
+handle_request(
+ boost::beast::string_view doc_root,
+ http::request>&& req,
+ Send&& send)
+{
+ // Returns a bad request response
+ auto const bad_request =
+ [&req](boost::beast::string_view why)
+ {
+ http::response res{http::status::bad_request, req.version};
+ res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
+ res.set(http::field::content_type, "text/html");
+ res.keep_alive(req.keep_alive());
+ res.body = why.to_string();
+ res.prepare_payload();
+ return res;
+ };
+
+ // Returns a not found response
+ auto const not_found =
+ [&req](boost::beast::string_view target)
+ {
+ http::response res{http::status::not_found, req.version};
+ res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
+ res.set(http::field::content_type, "text/html");
+ res.keep_alive(req.keep_alive());
+ res.body = "The resource '" + target.to_string() + "' was not found.";
+ res.prepare_payload();
+ return res;
+ };
+
+ // Returns a server error response
+ auto const server_error =
+ [&req](boost::beast::string_view what)
+ {
+ http::response res{http::status::internal_server_error, req.version};
+ res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
+ res.set(http::field::content_type, "text/html");
+ res.keep_alive(req.keep_alive());
+ res.body = "An error occurred: '" + what.to_string() + "'";
+ res.prepare_payload();
+ return res;
+ };
+
+ // Make sure we can handle the method
+ if( req.method() != http::verb::get &&
+ req.method() != http::verb::head)
+ return send(bad_request("Unknown HTTP-method"));
+
+ // Request path must be absolute and not contain "..".
+ if( req.target().empty() ||
+ req.target()[0] != '/' ||
+ req.target().find("..") != boost::beast::string_view::npos)
+ return send(bad_request("Illegal request-target"));
+
+ // Build the path to the requested file
+ std::string path = path_cat(doc_root, req.target());
+ if(req.target().back() == '/')
+ path.append("index.html");
+
+ // Attempt to open the file
+ boost::beast::error_code ec;
+ http::file_body::value_type body;
+ body.open(path.c_str(), boost::beast::file_mode::scan, ec);
+
+ // Handle the case where the file doesn't exist
+ if(ec == boost::system::errc::no_such_file_or_directory)
+ return send(not_found(req.target()));
+
+ // Handle an unknown error
+ if(ec)
+ return send(server_error(ec.message()));
+
+ // Respond to HEAD request
+ if(req.method() == http::verb::head)
+ {
+ http::response res{http::status::ok, req.version};
+ res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
+ res.set(http::field::content_type, mime_type(path));
+ res.content_length(body.size());
+ res.keep_alive(req.keep_alive());
+ return send(std::move(res));
+ }
+
+ // Respond to HEAD request
+ http::response res{
+ std::piecewise_construct,
+ std::make_tuple(std::move(body)),
+ std::make_tuple(http::status::ok, req.version)};
+ res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
+ res.set(http::field::content_type, mime_type(path));
+ res.content_length(body.size());
+ res.keep_alive(req.keep_alive());
+ return send(std::move(res));
+}
+
+//------------------------------------------------------------------------------
+
+// Report a failure
+void
+fail(boost::system::error_code ec, char const* what)
+{
+ std::cerr << what << ": " << ec.message() << "\n";
+}
+
+// Echoes back all received WebSocket messages
+class websocket_session : public std::enable_shared_from_this
+{
+ websocket::stream ws_;
+ boost::asio::io_service::strand strand_;
+ boost::asio::steady_timer timer_;
+ boost::beast::multi_buffer buffer_;
+
+public:
+ // Take ownership of the socket
+ explicit
+ websocket_session(tcp::socket socket)
+ : ws_(std::move(socket))
+ , strand_(ws_.get_io_service())
+ , timer_(ws_.get_io_service(),
+ (std::chrono::steady_clock::time_point::max)())
+ {
+ }
+
+ // Start the asynchronous operation
+ template
+ void
+ run(http::request> req)
+ {
+ // Run the timer. The timer is operated
+ // continuously, this simplifies the code.
+ on_timer({});
+
+ // Set the timer
+ timer_.expires_from_now(std::chrono::seconds(15));
+
+ // Accept the websocket handshake
+ ws_.async_accept(
+ req,
+ strand_.wrap(std::bind(
+ &websocket_session::on_accept,
+ shared_from_this(),
+ std::placeholders::_1)));
+ }
+
+ // Called when the timer expires.
+ void
+ on_timer(boost::system::error_code ec)
+ {
+ if(ec && ec != boost::asio::error::operation_aborted)
+ return fail(ec, "timer");
+
+ // Verify that the timer really expired since the deadline may have moved.
+ if(timer_.expires_at() <= std::chrono::steady_clock::now())
+ {
+ // Closing the socket cancels all outstanding operations. They
+ // will complete with boost::asio::error::operation_aborted
+ ws_.next_layer().shutdown(tcp::socket::shutdown_both, ec);
+ ws_.next_layer().close(ec);
+ return;
+ }
+
+ // Wait on the timer
+ timer_.async_wait(
+ strand_.wrap(std::bind(
+ &websocket_session::on_timer,
+ shared_from_this(),
+ std::placeholders::_1)));
+ }
+
+ void
+ on_accept(boost::system::error_code ec)
+ {
+ // Happens when the timer closes the socket
+ if(ec == boost::asio::error::operation_aborted)
+ return;
+
+ if(ec)
+ return fail(ec, "accept");
+
+ // Read a message
+ do_read();
+ }
+
+ void
+ do_read()
+ {
+ // Set the timer
+ timer_.expires_from_now(std::chrono::seconds(15));
+
+ // Read a message into our buffer
+ ws_.async_read(
+ buffer_,
+ strand_.wrap(std::bind(
+ &websocket_session::on_read,
+ shared_from_this(),
+ std::placeholders::_1)));
+ }
+
+ void
+ on_read(boost::system::error_code ec)
+ {
+ // Happens when the timer closes the socket
+ if(ec == boost::asio::error::operation_aborted)
+ return;
+
+ // This indicates that the websocket_session was closed
+ if(ec == websocket::error::closed)
+ return;
+
+ if(ec)
+ fail(ec, "read");
+
+ // Echo the message
+ ws_.text(ws_.got_text());
+ ws_.async_write(
+ buffer_.data(),
+ strand_.wrap(std::bind(
+ &websocket_session::on_write,
+ shared_from_this(),
+ std::placeholders::_1)));
+ }
+
+ void
+ on_write(boost::system::error_code ec)
+ {
+ // Happens when the timer closes the socket
+ if(ec == boost::asio::error::operation_aborted)
+ return;
+
+ if(ec)
+ return fail(ec, "write");
+
+ // Clear the buffer
+ buffer_.consume(buffer_.size());
+
+ // Do another read
+ do_read();
+ }
+};
+
+// Handles an HTTP server connection
+class http_session : public std::enable_shared_from_this
+{
+ // This queue is used for HTTP pipelining.
+ class queue
+ {
+ enum
+ {
+ // Maximum number of responses we will queue
+ limit = 8
+ };
+
+ // The type-erased, saved work item
+ struct work
+ {
+ virtual ~work() = default;
+ virtual void operator()() = 0;
+ };
+
+ bool busy_ = false; // true if a write is in progress
+ http_session& self_;
+ std::vector> items_;
+
+ public:
+ explicit
+ queue(http_session& self)
+ : self_(self)
+ {
+ static_assert(limit > 0, "queue limit must be positive");
+ items_.reserve(limit);
+ }
+
+ // Returns `true` if we have reached the queue limit
+ bool
+ is_full() const
+ {
+ return items_.size() + (busy_ ? 1 : 0) >= limit;
+ }
+
+ // Called when a message finishes sending
+ // Returns `true` if the caller should initiate a read
+ bool
+ on_write()
+ {
+ BOOST_ASSERT(busy_);
+ auto const do_read = items_.size() + (busy_ ? 1 : 0) >= limit;
+ if(! items_.empty())
+ {
+ (*items_.back())();
+ items_.erase(items_.begin());
+ }
+ else
+ {
+ busy_ = false;
+ }
+ return do_read;
+ }
+
+ // Called by the HTTP handler to send a response.
+ template
+ void
+ operator()(http::message&& msg)
+ {
+ // See if a write is in progress
+ if(! busy_)
+ {
+ // No write in progress so start one
+ busy_ = true;
+
+ // This function takes ownership of the message by moving
+ // it into a temporary buffer, otherwise we would have
+ // to manage the lifetime of the message and serializer.
+ return async_write_msg(
+ self_.socket_,
+ std::move(msg),
+ self_.strand_.wrap(std::bind(
+ &http_session::on_write,
+ self_.shared_from_this(),
+ std::placeholders::_1)));
+ }
+
+ // This holds a work item
+ struct work_impl : work
+ {
+ http_session& self_;
+ http::message msg_;
+
+ work_impl(
+ http_session& self,
+ http::message&& msg)
+ : self_(self)
+ , msg_(std::move(msg))
+ {
+ }
+
+ void
+ operator()()
+ {
+ async_write_msg(
+ self_.socket_,
+ std::move(msg_),
+ self_.strand_.wrap(std::bind(
+ &http_session::on_write,
+ self_.shared_from_this(),
+ std::placeholders::_1)));
+ }
+ };
+
+ // A write is in progress, so allocate storage to
+ // save this work item so we can invoke it later.
+ items_.emplace_back(new work_impl(self_, std::move(msg)));
+ }
+ };
+
+ tcp::socket socket_;
+ boost::asio::io_service::strand strand_;
+ boost::asio::steady_timer timer_;
+ boost::beast::flat_buffer buffer_;
+ std::string const& doc_root_;
+ http::request req_;
+ queue queue_;
+
+public:
+ // Take ownership of the socket
+ explicit
+ http_session(
+ tcp::socket socket,
+ std::string const& doc_root)
+ : socket_(std::move(socket))
+ , strand_(socket_.get_io_service())
+ , timer_(socket_.get_io_service(),
+ (std::chrono::steady_clock::time_point::max)())
+ , doc_root_(doc_root)
+ , queue_(*this)
+ {
+ }
+
+ // Start the asynchronous operation
+ void
+ run()
+ {
+ // Run the timer. The timer is operated
+ // continuously, this simplifies the code.
+ on_timer({});
+
+ do_read();
+ }
+
+ void
+ do_read()
+ {
+ // Set the timer
+ timer_.expires_from_now(std::chrono::seconds(15));
+
+ // Read a request
+ http::async_read(socket_, buffer_, req_,
+ strand_.wrap(std::bind(
+ &http_session::on_read,
+ shared_from_this(),
+ std::placeholders::_1)));
+ }
+
+ // Called when the timer expires.
+ void
+ on_timer(boost::system::error_code ec)
+ {
+ if(ec && ec != boost::asio::error::operation_aborted)
+ return fail(ec, "timer");
+
+ // Verify that the timer really expired since the deadline may have moved.
+ if(timer_.expires_at() <= std::chrono::steady_clock::now())
+ {
+ // Closing the socket cancels all outstanding operations. They
+ // will complete with boost::asio::error::operation_aborted
+ socket_.shutdown(tcp::socket::shutdown_both, ec);
+ socket_.close(ec);
+ return;
+ }
+
+ // Wait on the timer
+ timer_.async_wait(
+ strand_.wrap(std::bind(
+ &http_session::on_timer,
+ shared_from_this(),
+ std::placeholders::_1)));
+ }
+
+ void
+ on_read(boost::system::error_code ec)
+ {
+ // Happens when the timer closes the socket
+ if(ec == boost::asio::error::operation_aborted)
+ return;
+
+ // This means they closed the connection
+ if(ec == http::error::end_of_stream)
+ return do_close();
+
+ if(ec)
+ return fail(ec, "read");
+
+ // See if it is a WebSocket Upgrade
+ if(websocket::is_upgrade(req_))
+ {
+ // Create a WebSocket websocket_session by transferring the socket
+ std::make_shared(
+ std::move(socket_))->run(std::move(req_));
+ return;
+ }
+
+ // Send the response
+ handle_request(doc_root_, std::move(req_), queue_);
+
+ // If we aren't at the queue limit, try to pipeline another request
+ if(! queue_.is_full())
+ do_read();
+ }
+
+ void
+ on_write(boost::system::error_code ec)
+ {
+ // Happens when the timer closes the socket
+ if(ec == boost::asio::error::operation_aborted)
+ return;
+
+ if(ec == http::error::end_of_stream)
+ {
+ // This means we should close the connection, usually because
+ // the response indicated the "Connection: close" semantic.
+ return do_close();
+ }
+
+ if(ec)
+ return fail(ec, "write");
+
+ // Inform the queue that a write completed
+ if(queue_.on_write())
+ {
+ // Read another request
+ do_read();
+ }
+ }
+
+ void
+ do_close()
+ {
+ // Send a TCP shutdown
+ boost::system::error_code ec;
+ socket_.shutdown(tcp::socket::shutdown_send, ec);
+
+ // At this point the connection is closed gracefully
+ }
+};
+
+//------------------------------------------------------------------------------
+
+// Accepts incoming connections and launches the sessions
+class listener : public std::enable_shared_from_this
+{
+ boost::asio::io_service::strand strand_;
+ tcp::acceptor acceptor_;
+ tcp::socket socket_;
+ std::string const& doc_root_;
+
+public:
+ listener(
+ boost::asio::io_service& ios,
+ tcp::endpoint endpoint,
+ std::string const& doc_root)
+ : strand_(ios)
+ , acceptor_(ios)
+ , socket_(ios)
+ , doc_root_(doc_root)
+ {
+ boost::system::error_code ec;
+
+ // Open the acceptor
+ acceptor_.open(endpoint.protocol(), ec);
+ if(ec)
+ {
+ fail(ec, "open");
+ return;
+ }
+
+ // Bind to the server address
+ acceptor_.bind(endpoint, ec);
+ if(ec)
+ {
+ fail(ec, "bind");
+ return;
+ }
+
+ // Start listening for connections
+ acceptor_.listen(
+ boost::asio::socket_base::max_connections, ec);
+ if(ec)
+ {
+ fail(ec, "listen");
+ return;
+ }
+ }
+
+ // Start accepting incoming connections
+ void
+ run()
+ {
+ if(! acceptor_.is_open())
+ return;
+ do_accept();
+ }
+
+ void
+ do_accept()
+ {
+ acceptor_.async_accept(
+ socket_,
+ std::bind(
+ &listener::on_accept,
+ shared_from_this(),
+ std::placeholders::_1));
+ }
+
+ void
+ on_accept(boost::system::error_code ec)
+ {
+ if(ec)
+ {
+ fail(ec, "accept");
+ }
+ else
+ {
+ // Create the http_session and run it
+ std::make_shared(
+ std::move(socket_),
+ doc_root_)->run();
+ }
+
+ // Accept another connection
+ do_accept();
+ }
+};
+
+//------------------------------------------------------------------------------
+
+int main(int argc, char* argv[])
+{
+ // Check command line arguments.
+ if (argc != 5)
+ {
+ std::cerr <<
+ "Usage: advanced-server \n" <<
+ "Example:\n" <<
+ " advanced-server 0.0.0.0 8080 . 1\n";
+ return EXIT_FAILURE;
+ }
+ auto const address = boost::asio::ip::address::from_string(argv[1]);
+ auto const port = static_cast(std::atoi(argv[2]));
+ std::string const doc_root = argv[3];
+ auto const threads = std::max(1, std::atoi(argv[3]));
+
+ // The io_service is required for all I/O
+ boost::asio::io_service ios{threads};
+
+ // Create and launch a listening port
+ std::make_shared(
+ ios,
+ tcp::endpoint{address, port},
+ doc_root)->run();
+
+ // Run the I/O service on the requested number of threads
+ std::vector v;
+ v.reserve(threads - 1);
+ for(auto i = threads - 1; i > 0; --i)
+ v.emplace_back(
+ [&ios]
+ {
+ ios.run();
+ });
+ ios.run();
+
+ return EXIT_SUCCESS;
+}
diff --git a/example/common/detect_ssl.hpp b/example/common/detect_ssl.hpp
index 00693d13..9018ebdc 100644
--- a/example/common/detect_ssl.hpp
+++ b/example/common/detect_ssl.hpp
@@ -21,7 +21,7 @@
//[example_core_detect_ssl_1
-#include
+#include
#include
/** Return `true` if a buffer contains a TLS/SSL client handshake.
@@ -52,8 +52,6 @@ is_ssl_handshake(ConstBufferSequence const& buffers);
//]
-using namespace boost::beast;
-
//[example_core_detect_ssl_2
template<
@@ -63,7 +61,7 @@ is_ssl_handshake(
ConstBufferSequence const& buffers)
{
// Make sure buffers meets the requirements
- static_assert(is_const_buffer_sequence::value,
+ static_assert(boost::beast::is_const_buffer_sequence::value,
"ConstBufferSequence requirements not met");
// We need at least one byte to really do anything
@@ -130,12 +128,14 @@ boost::tribool
detect_ssl(
SyncReadStream& stream,
DynamicBuffer& buffer,
- error_code& ec)
+ boost::beast::error_code& ec)
{
+ namespace beast = boost::beast;
+
// Make sure arguments meet the requirements
- static_assert(is_sync_read_stream::value,
+ static_assert(beast::is_sync_read_stream::value,
"SyncReadStream requirements not met");
- static_assert(is_dynamic_buffer::value,
+ static_assert(beast::is_dynamic_buffer::value,
"DynamicBuffer requirements not met");
// Loop until an error occurs or we get a definitive answer
@@ -148,15 +148,17 @@ detect_ssl(
// If we got an answer, return it
if(! boost::indeterminate(result))
{
- ec = {}; // indicate no error
+ // This is a fast way to indicate success
+ // without retrieving the default category.
+ ec.assign(0, ec.category());
return result;
}
// The algorithm should never need more than 4 bytes
BOOST_ASSERT(buffer.size() < 4);
- // Create up to 4 bytes of space in the buffer's output area.
- auto const mutable_buffer = buffer.prepare(4 - buffer.size());
+ // Prepare the buffer's output area.
+ auto const mutable_buffer = buffer.prepare(beast::read_size(buffer, 1536));
// Try to fill our buffer by reading from the stream
std::size_t const bytes_transferred = stream.read_some(mutable_buffer, ec);
@@ -223,9 +225,9 @@ template<
class AsyncReadStream,
class DynamicBuffer,
class CompletionToken>
-async_return_type< /*< The [link beast.ref.boost__beast__async_return_type `async_return_type`] customizes the return value based on the completion token >*/
+boost::beast::async_return_type< /*< The [link beast.ref.boost__beast__async_return_type `async_return_type`] customizes the return value based on the completion token >*/
CompletionToken,
- void(error_code, boost::tribool)> /*< This is the signature for the completion handler >*/
+ void(boost::beast::error_code, boost::tribool)> /*< This is the signature for the completion handler >*/
async_detect_ssl(
AsyncReadStream& stream,
DynamicBuffer& buffer,
@@ -247,26 +249,28 @@ template<
class AsyncReadStream,
class DynamicBuffer,
class CompletionToken>
-async_return_type<
+boost::beast::async_return_type<
CompletionToken,
- void(error_code, boost::tribool)>
+ void(boost::beast::error_code, boost::tribool)>
async_detect_ssl(
AsyncReadStream& stream,
DynamicBuffer& buffer,
CompletionToken&& token)
{
+ namespace beast = boost::beast;
+
// Make sure arguments meet the requirements
- static_assert(is_async_read_stream::value,
+ static_assert(beast::is_async_read_stream::value,
"SyncReadStream requirements not met");
- static_assert(is_dynamic_buffer::value,
+ static_assert(beast::is_dynamic_buffer::value,
"DynamicBuffer requirements not met");
// This helper manages some of the handler's lifetime and
// uses the result and handler specializations associated with
// the completion token to help customize the return value.
//
- boost::beast::async_completion<
- CompletionToken, void(boost::beast::error_code, boost::tribool)> init{token};
+ beast::async_completion<
+ CompletionToken, void(beast::error_code, boost::tribool)> init{token};
// Create the composed operation and launch it. This is a constructor
// call followed by invocation of operator(). We use handler_type
@@ -274,10 +278,10 @@ async_detect_ssl(
// allowing user defined specializations of the async result template
// to take effect.
//
- detect_ssl_op>{
+ detect_ssl_op>{
stream, buffer, init.completion_handler}(
- boost::beast::error_code{}, 0);
+ beast::error_code{}, 0);
// This hook lets the caller see a return value when appropriate.
// For example this might return std::future if
@@ -322,8 +326,10 @@ public:
// The constructor just keeps references the callers varaibles.
//
template
- detect_ssl_op(AsyncReadStream& stream,
- DynamicBuffer& buffer, DeducedHandler&& handler)
+ detect_ssl_op(
+ AsyncReadStream& stream,
+ DynamicBuffer& buffer,
+ DeducedHandler&& handler)
: stream_(stream)
, buffer_(buffer)
, handler_(std::forward(handler))
@@ -402,6 +408,8 @@ void
detect_ssl_op::
operator()(boost::beast::error_code ec, std::size_t bytes_transferred)
{
+ namespace beast = boost::beast;
+
// Execute the state machine
switch(step_)
{
@@ -422,7 +430,7 @@ operator()(boost::beast::error_code ec, std::size_t bytes_transferred)
// original handler.
step_ = 1;
return stream_.get_io_service().post(
- bind_handler(std::move(*this), ec, 0));
+ beast::bind_handler(std::move(*this), ec, 0));
}
// The algorithm should never need more than 4 bytes
@@ -432,7 +440,7 @@ operator()(boost::beast::error_code ec, std::size_t bytes_transferred)
do_read:
// We need more bytes, but no more than four total.
- return stream_.async_read_some(buffer_.prepare(4 - buffer_.size()), std::move(*this));
+ return stream_.async_read_some(buffer_.prepare(beast::read_size(buffer_, 1536)), std::move(*this));
case 1:
// Call the handler
diff --git a/example/common/helpers.hpp b/example/common/helpers.hpp
deleted file mode 100644
index df2b954d..00000000
--- a/example/common/helpers.hpp
+++ /dev/null
@@ -1,58 +0,0 @@
-//
-// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/boostorg/beast
-//
-
-#ifndef BOOST_BEAST_EXAMPLE_COMMON_HELPERS_HPP
-#define BOOST_BEAST_EXAMPLE_COMMON_HELPERS_HPP
-
-#include
-#include
-#include
-#include
-
-/// Block until SIGINT or SIGTERM is received.
-inline
-void
-sig_wait()
-{
- boost::asio::io_service ios{1};
- boost::asio::signal_set signals(ios, SIGINT, SIGTERM);
- signals.async_wait([&](boost::system::error_code const&, int){});
- ios.run();
-}
-
-namespace detail {
-
-inline
-void
-print_1(std::ostream&)
-{
-}
-
-template
-void
-print_1(std::ostream& os, T1 const& t1, TN const&... tn)
-{
- os << t1;
- print_1(os, tn...);
-}
-
-} // detail
-
-// compose a string to std::cout or std::cerr atomically
-//
-template
-void
-print(std::ostream& os, Args const&... args)
-{
- std::stringstream ss;
- detail::print_1(ss, args...);
- os << ss.str() << std::endl;
-}
-
-#endif
diff --git a/example/common/mime_types.hpp b/example/common/mime_types.hpp
deleted file mode 100644
index 8bb654c9..00000000
--- a/example/common/mime_types.hpp
+++ /dev/null
@@ -1,48 +0,0 @@
-//
-// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/boostorg/beast
-//
-
-#ifndef BOOST_BEAST_EXAMPLE_COMMON_MIME_TYPES_HPP
-#define BOOST_BEAST_EXAMPLE_COMMON_MIME_TYPES_HPP
-
-#include
-#include
-
-// Return a reasonable mime type based on the extension of a file.
-//
-template
-boost::beast::string_view
-mime_type(boost::filesystem::path const& path)
-{
- using boost::beast::iequals;
- auto const ext = path.extension().string();
- if(iequals(ext, ".txt")) return "text/plain";
- if(iequals(ext, ".htm")) return "text/html";
- if(iequals(ext, ".html")) return "text/html";
- if(iequals(ext, ".php")) return "text/html";
- if(iequals(ext, ".css")) return "text/css";
- if(iequals(ext, ".js")) return "application/javascript";
- if(iequals(ext, ".json")) return "application/json";
- if(iequals(ext, ".xml")) return "application/xml";
- if(iequals(ext, ".swf")) return "application/x-shockwave-flash";
- if(iequals(ext, ".flv")) return "video/x-flv";
- if(iequals(ext, ".png")) return "image/png";
- if(iequals(ext, ".jpe")) return "image/jpeg";
- if(iequals(ext, ".jpeg")) return "image/jpeg";
- if(iequals(ext, ".jpg")) return "image/jpeg";
- if(iequals(ext, ".gif")) return "image/gif";
- if(iequals(ext, ".bmp")) return "image/bmp";
- if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon";
- if(iequals(ext, ".tiff")) return "image/tiff";
- if(iequals(ext, ".tif")) return "image/tiff";
- if(iequals(ext, ".svg")) return "image/svg+xml";
- if(iequals(ext, ".svgz")) return "image/svg+xml";
- return "application/text";
-}
-
-#endif
diff --git a/example/common/rfc7231.hpp b/example/common/rfc7231.hpp
deleted file mode 100644
index 50726079..00000000
--- a/example/common/rfc7231.hpp
+++ /dev/null
@@ -1,41 +0,0 @@
-//
-// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/boostorg/beast
-//
-
-#ifndef BOOST_BEAST_EXAMPLE_COMMON_RFC7231_HPP
-#define BOOST_BEAST_EXAMPLE_COMMON_RFC7231_HPP
-
-#include
-#include
-
-namespace rfc7231 {
-
-// This aggregates a collection of algorithms
-// corresponding to specifications in rfc7231:
-//
-// https://tools.ietf.org/html/rfc7231
-//
-
-/** Returns `true` if the message specifies Expect: 100-continue
-
- @param req The request to check
-
- @see https://tools.ietf.org/html/rfc7231#section-5.1.1
-*/
-template
-bool
-is_expect_100_continue(boost::beast::http::request<
- Body, boost::beast::http::basic_fields> const& req)
-{
- return boost::beast::iequals(
- req[boost::beast::http::field::expect], "100-continue");
-}
-
-} // rfc7231
-
-#endif
diff --git a/example/common/root_certificates.hpp b/example/common/root_certificates.hpp
index 706ca701..a5a4dbb0 100644
--- a/example/common/root_certificates.hpp
+++ b/example/common/root_certificates.hpp
@@ -110,6 +110,7 @@ load_root_certificates(ssl::context& ctx, boost::system::error_code& ec)
// gratuituous template argument; thus it appears
// like a "normal" function.
//
+
inline
void
load_root_certificates(ssl::context& ctx, boost::system::error_code& ec)
@@ -117,4 +118,14 @@ load_root_certificates(ssl::context& ctx, boost::system::error_code& ec)
detail::load_root_certificates(ctx, ec);
}
+inline
+void
+load_root_certificates(ssl::context& ctx)
+{
+ boost::system::error_code ec;
+ detail::load_root_certificates(ctx, ec);
+ if(ec)
+ throw boost::system::system_error{ec};
+}
+
#endif
diff --git a/example/server-framework/ssl_certificate.hpp b/example/common/server_certificate.hpp
similarity index 86%
rename from example/server-framework/ssl_certificate.hpp
rename to example/common/server_certificate.hpp
index 6bddcb47..fa0926bc 100644
--- a/example/server-framework/ssl_certificate.hpp
+++ b/example/common/server_certificate.hpp
@@ -7,48 +7,26 @@
// Official repository: https://github.com/boostorg/beast
//
-#ifndef BOOST_BEAST_EXAMPLE_SERVER_SSL_CERTIFICATE_HPP
-#define BOOST_BEAST_EXAMPLE_SERVER_SSL_CERTIFICATE_HPP
+#ifndef BOOST_BEAST_EXAMPLE_COMMON_SERVER_CERTIFICATE_HPP
+#define BOOST_BEAST_EXAMPLE_COMMON_SERVER_CERTIFICATE_HPP
#include
#include
#include
#include
-namespace framework {
+/* Load a signed certificate into the ssl context, and configure
+ the context for use with a server.
-// This sets up the self-signed certificate that the server
-// uses for its encrypted connections
-
-class ssl_certificate
-{
- // The template argument is gratuitous, to
- // make the definition header-only without
- // also making it inline.
- //
- template
- void
- construct();
-
- boost::asio::ssl::context ctx_;
-
-public:
- ssl_certificate()
- : ctx_(boost::asio::ssl::context::sslv23)
- {
- construct();
- }
-
- boost::asio::ssl::context&
- get()
- {
- return ctx_;
- }
-};
-
-template
+ For this to work with the browser or operating system, it is
+ necessary to import the "Beast Test CA" certificate into
+ the local certificate store, browser, or operating system
+ depending on your environment Please see the documentation
+ accompanying the Beast certificate for more details.
+*/
+inline
void
-ssl_certificate::construct()
+load_server_certificate(boost::asio::ssl::context& ctx)
{
/*
The certificate was generated from CMD.EXE on Windows 10 using:
@@ -120,29 +98,27 @@ ssl_certificate::construct()
"QMUk26jPTIVTLfXmmwU0u8vUkpR7LQKkwwIBAg==\n"
"-----END DH PARAMETERS-----\n";
- ctx_.set_password_callback(
+ ctx.set_password_callback(
[](std::size_t,
boost::asio::ssl::context_base::password_purpose)
{
return "test";
});
- ctx_.set_options(
+ ctx.set_options(
boost::asio::ssl::context::default_workarounds |
boost::asio::ssl::context::no_sslv2 |
boost::asio::ssl::context::single_dh_use);
- ctx_.use_certificate_chain(
+ ctx.use_certificate_chain(
boost::asio::buffer(cert.data(), cert.size()));
- ctx_.use_private_key(
+ ctx.use_private_key(
boost::asio::buffer(key.data(), key.size()),
boost::asio::ssl::context::file_format::pem);
- ctx_.use_tmp_dh(
+ ctx.use_tmp_dh(
boost::asio::buffer(dh.data(), dh.size()));
}
-} // framework
-
#endif
diff --git a/example/common/ssl_stream.hpp b/example/common/ssl_stream.hpp
index 2abbf06e..b4dabd2b 100644
--- a/example/common/ssl_stream.hpp
+++ b/example/common/ssl_stream.hpp
@@ -61,11 +61,13 @@ public:
/// The type of the lowest layer.
using lowest_layer_type = typename stream_type::lowest_layer_type;
- ssl_stream(boost::asio::ip::tcp::socket&& sock, boost::asio::ssl::context& ctx)
- : p_(new stream_type{sock.get_io_service(), ctx})
+ ssl_stream(
+ boost::asio::ip::tcp::socket socket,
+ boost::asio::ssl::context& ctx)
+ : p_(new stream_type{socket.get_io_service(), ctx})
, ctx_(&ctx)
{
- p_->next_layer() = std::move(sock);
+ p_->next_layer() = std::move(socket);
}
ssl_stream(ssl_stream&& other)
diff --git a/example/http-client-ssl/Jamfile b/example/http-client-ssl/Jamfile
deleted file mode 100644
index 4d9f9fcc..00000000
--- a/example/http-client-ssl/Jamfile
+++ /dev/null
@@ -1,53 +0,0 @@
-#
-# Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
-#
-# Distributed under the Boost Software License, Version 1.0. (See accompanying
-# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-#
-# Official repository: https://github.com/boostorg/beast
-#
-
-import os ;
-
-if [ os.name ] = SOLARIS
-{
- lib socket ;
- lib nsl ;
-}
-else if [ os.name ] = NT
-{
- lib ws2_32 ;
- lib mswsock ;
-}
-else if [ os.name ] = HPUX
-{
- lib ipv6 ;
-}
-else if [ os.name ] = HAIKU
-{
- lib network ;
-}
-
-if [ os.name ] = NT
-{
- lib ssl : : ssleay32 ;
- lib crypto : : libeay32 ;
-}
-else
-{
- lib ssl ;
- lib crypto ;
-}
-
-project
- : requirements
- ssl
- crypto
- ;
-
-exe http-client-ssl :
- http_client_ssl.cpp
- :
- coverage:no
- ubasan:no
- ;
diff --git a/example/http-client-ssl/http_client_ssl.cpp b/example/http-client-ssl/http_client_ssl.cpp
deleted file mode 100644
index 05be692a..00000000
--- a/example/http-client-ssl/http_client_ssl.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-//
-// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/boostorg/beast
-//
-
-#include "../common/root_certificates.hpp"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-using tcp = boost::asio::ip::tcp; // from
-namespace ssl = boost::asio::ssl; // from
-namespace http = boost::beast::http; // from
-
-int main()
-{
- // A helper for reporting errors
- auto const fail =
- [](std::string what, boost::beast::error_code ec)
- {
- std::cerr << what << ": " << ec.message() << std::endl;
- std::cerr.flush();
- return EXIT_FAILURE;
- };
-
- boost::system::error_code ec;
-
- // Normal boost::asio setup
- boost::asio::io_service ios;
- tcp::resolver r{ios};
- tcp::socket sock{ios};
-
- // Look up the domain name
- std::string const host = "www.example.com";
- auto const lookup = r.resolve({host, "https"}, ec);
- if(ec)
- return fail("resolve", ec);
-
- // Make the connection on the IP address we get from a lookup
- boost::asio::connect(sock, lookup, ec);
- if(ec)
- return fail("connect", ec);
-
- // Create the required ssl context
- ssl::context ctx{ssl::context::sslv23_client};
-
- // This holds the root certificate used for verification
- load_root_certificates(ctx, ec);
- if(ec)
- return fail("certificate", ec);
-
- // Wrap the now-connected socket in an SSL stream
- ssl::stream stream{sock, ctx};
- stream.set_verify_mode(ssl::verify_peer | ssl::verify_fail_if_no_peer_cert);
-
- // Perform SSL handshaking
- stream.handshake(ssl::stream_base::client, ec);
- if(ec)
- return fail("handshake", ec);
-
- // Set up an HTTP GET request message
- http::request req;
- req.method(http::verb::get);
- req.target("/");
- req.version = 11;
- req.set(http::field::host, host + ":" +
- std::to_string(sock.remote_endpoint().port()));
- req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
- req.prepare_payload();
-
- // Write the HTTP request to the remote host
- http::write(stream, req, ec);
- if(ec)
- return fail("write", ec);
-
- // This buffer is used for reading and must be persisted
- boost::beast::flat_buffer b;
-
- // Declare a container to hold the response
- http::response res;
-
- // Read the response
- http::read(stream, b, res, ec);
- if(ec)
- return fail("read", ec);
-
- // Write the message to standard out
- std::cout << res << std::endl;
-
- // Shut down SSL on the stream
- stream.shutdown(ec);
- if(ec && ec != boost::asio::error::eof)
- fail("ssl_shutdown ", ec);
-
- // If we get here then the connection is closed gracefully
- return EXIT_SUCCESS;
-}
diff --git a/example/http-client/http_client.cpp b/example/http-client/http_client.cpp
deleted file mode 100644
index ff31e151..00000000
--- a/example/http-client/http_client.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-//
-// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/boostorg/beast
-
-//[example_http_client
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-using tcp = boost::asio::ip::tcp; // from
-namespace http = boost::beast::http; // from
-
-int main()
-{
- // A helper for reporting errors
- auto const fail =
- [](std::string what, boost::beast::error_code ec)
- {
- std::cerr << what << ": " << ec.message() << std::endl;
- return EXIT_FAILURE;
- };
-
- boost::beast::error_code ec;
-
- // Set up an asio socket
- boost::asio::io_service ios;
- tcp::resolver r{ios};
- tcp::socket sock{ios};
-
- // Look up the domain name
- std::string const host = "www.example.com";
- auto const lookup = r.resolve({host, "http"}, ec);
- if(ec)
- return fail("resolve", ec);
-
- // Make the connection on the IP address we get from a lookup
- boost::asio::connect(sock, lookup, ec);
- if(ec)
- return fail("connect", ec);
-
- // Set up an HTTP GET request message
- http::request req{http::verb::get, "/", 11};
- req.set(http::field::host, host + ":" +
- std::to_string(sock.remote_endpoint().port()));
- req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
- req.prepare_payload();
-
- // Write the HTTP request to the remote host
- http::write(sock, req, ec);
- if(ec)
- return fail("write", ec);
-
- // This buffer is used for reading and must be persisted
- boost::beast::flat_buffer b;
-
- // Declare a container to hold the response
- http::response res;
-
- // Read the response
- http::read(sock, b, res, ec);
- if(ec)
- return fail("read", ec);
-
- // Write the message to standard out
- std::cout << res << std::endl;
-
- // Gracefully close the socket
- sock.shutdown(tcp::socket::shutdown_both, ec);
-
- // not_connected happens sometimes
- // so don't bother reporting it.
- //
- if(ec && ec != boost::beast::errc::not_connected)
- return fail("shutdown", ec);
-
- // If we get here then the connection is closed gracefully
- return EXIT_SUCCESS;
-}
-
-//]
diff --git a/example/http-crawl/http_crawl.cpp b/example/http-crawl/http_crawl.cpp
deleted file mode 100644
index 7883b566..00000000
--- a/example/http-crawl/http_crawl.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-//
-// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/boostorg/beast
-//
-
-#include "urls_large_data.hpp"
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-using tcp = boost::asio::ip::tcp; // from
-namespace http = boost::beast::http; // from
-
-template
-void
-err(boost::beast::error_code const& ec, String const& what)
-{
- std::cerr << what << ": " << ec.message() << std::endl;
-}
-
-/* This simple program just visits a list with a few
- thousand domain names and tries to retrieve and print
- the home page of each site.
-*/
-int
-main(int, char const*[])
-{
- // A helper for reporting errors
- auto const fail =
- [](std::string what, boost::beast::error_code ec)
- {
- std::cerr << what << ": " << ec.message() << std::endl;
- std::cerr.flush();
- return EXIT_FAILURE;
- };
-
- // Obligatory Asio variable
- boost::asio::io_service ios;
-
- // Loop over all the URLs
- for(auto const& host : urls_large_data())
- {
- boost::beast::error_code ec;
-
- // Look up the domain name
- tcp::resolver r(ios);
- auto lookup = r.resolve({host, "http"}, ec);
- if(ec)
- {
- fail("resolve", ec);
- continue;
- }
-
- // Now create a socket and connect
- tcp::socket sock(ios);
- boost::asio::connect(sock, lookup, ec);
- if(ec)
- {
- fail("connect", ec);
- continue;
- }
-
- // Grab the remote endpoint
- auto ep = sock.remote_endpoint(ec);
- if(ec)
- {
- fail("remote_endpoint", ec);
- continue;
- }
-
- // Set up an HTTP GET request
- http::request req{http::verb::get, "/", 11};
- req.set(http::field::host, host + std::string(":") + std::to_string(ep.port()));
- req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
-
- // Set the Connection: close field, this way the server will close
- // the connection. This consumes less resources (no TIME_WAIT) because
- // of the graceful close. It also makes things go a little faster.
- //
- req.set(http::field::connection, "close");
-
- // Send the GET request
- http::write(sock, req, ec);
- if(ec == http::error::end_of_stream)
- {
- // This special error received on a write indicates that the
- // semantics of the sent message are such that the connection
- // should be closed after the response is done. We do a TCP/IP
- // "half-close" here to shut down our end.
- //
- sock.shutdown(tcp::socket::shutdown_send, ec);
- if(ec && ec != boost::beast::errc::not_connected)
- return fail("shutdown", ec);
- }
- if(ec)
- {
- fail("write", ec);
- continue;
- }
-
- // This buffer is needed for reading
- boost::beast::multi_buffer b;
-
- // The response will go into this object
- http::response res;
-
- // Read the response
- http::read(sock, b, res, ec);
- if(ec == http::error::end_of_stream)
- {
- // This special error means that the other end closed the socket,
- // which is what we want since we asked for Connection: close.
- // However, we are going through a rather large number of servers
- // and sometimes they misbehave.
- ec = {};
- }
- else if(ec)
- {
- fail("read", ec);
- continue;
- }
-
- // Now we do the other half of the close,
- // which is to shut down the receiver.
- sock.shutdown(tcp::socket::shutdown_receive, ec);
- if(ec && ec != boost::beast::errc::not_connected)
- return fail("shutdown", ec);
-
- std::cout << res << std::endl;
- }
-}
diff --git a/example/http-server-threaded/http_server_threaded.cpp b/example/http-server-threaded/http_server_threaded.cpp
deleted file mode 100644
index c22deab4..00000000
--- a/example/http-server-threaded/http_server_threaded.cpp
+++ /dev/null
@@ -1,229 +0,0 @@
-//
-// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/boostorg/beast
-//
-
-#include "../common/mime_types.hpp"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-//------------------------------------------------------------------------------
-//
-// Example: HTTP server, synchronous, one thread per connection
-//
-//------------------------------------------------------------------------------
-
-namespace ip = boost::asio::ip; // from
-using tcp = boost::asio::ip::tcp; // from
-namespace http = boost::beast::http; // from
-
-class connection
- : public std::enable_shared_from_this
-{
- tcp::socket sock_;
- boost::beast::string_view root_;
-
-public:
- explicit
- connection(tcp::socket&& sock, boost::beast::string_view root)
- : sock_(std::move(sock))
- , root_(root)
- {
- }
-
- void
- run()
- {
- // Bind a shared_ptr to *this into the thread.
- // When the thread exits, the connection object
- // will be destroyed.
- //
- std::thread{&connection::do_run, shared_from_this()}.detach();
- }
-
-private:
- // Send a client error response
- http::response>
- client_error(http::status result, boost::beast::string_view text)
- {
- http::response> res{result, 11};
- res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
- res.set(http::field::content_type, "text/plain");
- res.set(http::field::connection, "close");
- res.body = text;
- res.prepare_payload();
- return res;
- }
-
- // Return an HTTP Not Found response
- //
- http::response
- not_found() const
- {
- http::response res{http::status::not_found, 11};
- res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
- res.set(http::field::content_type, "text/html");
- res.set(http::field::connection, "close");
- res.body = "The file was not found";
- res.prepare_payload();
- return res;
- }
-
- // Return an HTTP Server Error
- //
- http::response
- server_error(boost::beast::error_code const& ec) const
- {
- http::response res{http::status::internal_server_error, 11};
- res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
- res.set(http::field::content_type, "text/html");
- res.set(http::field::connection, "close");
- res.body = "Error: " + ec.message();
- res.prepare_payload();
- return res;
- }
-
- // Return a file response to an HTTP GET request
- //
- http::response
- get(boost::filesystem::path const& full_path,
- boost::beast::error_code& ec) const
- {
- http::response res;
- res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
- res.set(http::field::content_type, mime_type(full_path));
- res.set(http::field::connection, "close");
- res.body.open(full_path.string().c_str(), boost::beast::file_mode::scan, ec);
- if(ec)
- return res;
- res.set(http::field::content_length, res.body.size());
- return res;
- }
-
- // Handle a request
- template
- void
- do_request(http::request const& req, boost::beast::error_code& ec)
- {
- // verb must be get
- if(req.method() != http::verb::get)
- {
- http::write(sock_, client_error(http::status::bad_request, "Unsupported method"), ec);
- return;
- }
-
- // Request path must be absolute and not contain "..".
- if( req.target().empty() ||
- req.target()[0] != '/' ||
- req.target().find("..") != std::string::npos)
- {
- http::write(sock_, client_error(http::status::not_found, "File not found"), ec);
- return;
- }
-
- auto full_path = root_.to_string();
- full_path.append(req.target().data(), req.target().size());
-
- boost::beast::error_code file_ec;
- auto res = get(full_path, file_ec);
-
- if(file_ec == boost::beast::errc::no_such_file_or_directory)
- {
- http::write(sock_, not_found(), ec);
- }
- else if(ec)
- {
- http::write(sock_, server_error(file_ec), ec);
- }
- else
- {
- http::serializer sr{res};
- http::write(sock_, sr, ec);
- }
- }
-
- void
- do_run()
- {
- try
- {
- boost::beast::error_code ec;
- boost::beast::flat_buffer buffer;
- for(;;)
- {
- http::request_parser parser;
- parser.header_limit(8192);
- parser.body_limit(1024 * 1024);
- http::read(sock_, buffer, parser, ec);
- if(ec == http::error::end_of_stream)
- break;
- if(ec)
- throw boost::beast::system_error{ec};
- do_request(parser.get(), ec);
- if(ec)
- {
- if(ec != http::error::end_of_stream)
- throw boost::beast::system_error{ec};
- break;
- }
- }
- sock_.shutdown(tcp::socket::shutdown_both, ec);
- if(ec && ec != boost::asio::error::not_connected)
- throw boost::beast::system_error{ec};
- }
- catch (const std::exception& e)
- {
- std::cerr << "Exception: " << e.what() << std::endl;
- }
- }
-};
-
-int main(int argc, char* argv[])
-{
- try
- {
- // Check command line arguments.
- if (argc != 4)
- {
- std::cerr << "Usage: http_server \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 EXIT_FAILURE;
- }
-
- auto address = ip::address::from_string(argv[1]);
- unsigned short port = static_cast(std::atoi(argv[2]));
- std::string doc_root = argv[3];
-
- boost::asio::io_service ios{1};
- tcp::acceptor acceptor{ios, {address, port}};
- for(;;)
- {
- tcp::socket sock{ios};
- acceptor.accept(sock);
- std::make_shared(std::move(sock), doc_root)->run();
- }
- }
- catch (const std::exception& e)
- {
- std::cerr << "Exception: " << e.what() << std::endl;
- return EXIT_FAILURE;
- }
-}
diff --git a/example/http/CMakeLists.txt b/example/http/CMakeLists.txt
new file mode 100644
index 00000000..d38604dd
--- /dev/null
+++ b/example/http/CMakeLists.txt
@@ -0,0 +1,11 @@
+#
+# Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+#
+# Distributed under the Boost Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#
+# Official repository: https://github.com/boostorg/beast
+#
+
+add_subdirectory (client)
+add_subdirectory (server)
diff --git a/example/http/Jamfile b/example/http/Jamfile
new file mode 100644
index 00000000..32f2c514
--- /dev/null
+++ b/example/http/Jamfile
@@ -0,0 +1,11 @@
+#
+# Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+#
+# Distributed under the Boost Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#
+# Official repository: https://github.com/boostorg/beast
+#
+
+build-project client ;
+build-project server ;
diff --git a/example/http/client/CMakeLists.txt b/example/http/client/CMakeLists.txt
new file mode 100644
index 00000000..a8c09df0
--- /dev/null
+++ b/example/http/client/CMakeLists.txt
@@ -0,0 +1,19 @@
+#
+# Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+#
+# Distributed under the Boost Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#
+# Official repository: https://github.com/boostorg/beast
+#
+
+add_subdirectory (async)
+add_subdirectory (coro)
+add_subdirectory (crawl)
+add_subdirectory (sync)
+
+if (OPENSSL_FOUND)
+ add_subdirectory (async-ssl)
+ add_subdirectory (coro-ssl)
+ add_subdirectory (sync-ssl)
+endif()
diff --git a/example/http/client/Jamfile b/example/http/client/Jamfile
new file mode 100644
index 00000000..c5d71aab
--- /dev/null
+++ b/example/http/client/Jamfile
@@ -0,0 +1,18 @@
+#
+# Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+#
+# Distributed under the Boost Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#
+# Official repository: https://github.com/boostorg/beast
+#
+
+build-project async ;
+build-project coro ;
+build-project crawl ;
+build-project sync ;
+
+# VFALCO How do I make this work on Windows and if OpenSSL is not available?
+#build-project async-ssl ;
+#build-project coro-ssl ;
+#build-project sync-ssl ;
diff --git a/example/http/client/async-ssl/CMakeLists.txt b/example/http/client/async-ssl/CMakeLists.txt
new file mode 100644
index 00000000..c4bfec12
--- /dev/null
+++ b/example/http/client/async-ssl/CMakeLists.txt
@@ -0,0 +1,23 @@
+#
+# Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+#
+# Distributed under the Boost Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#
+# Official repository: https://github.com/boostorg/beast
+#
+
+GroupSources(include/boost/beast beast)
+GroupSources(example/common common)
+GroupSources(example/http/client/async-ssl "/")
+
+add_executable (http-client-async-ssl
+ ${BOOST_BEAST_INCLUDES}
+ ${PROJECT_SOURCE_DIR}/example/common/root_certificates.hpp
+ Jamfile
+ http_client_async_ssl.cpp
+)
+
+target_link_libraries (http-client-async-ssl
+ ${OPENSSL_LIBRARIES}
+ )
diff --git a/example/http/client/async-ssl/Jamfile b/example/http/client/async-ssl/Jamfile
new file mode 100644
index 00000000..ed5596af
--- /dev/null
+++ b/example/http/client/async-ssl/Jamfile
@@ -0,0 +1,21 @@
+#
+# Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+#
+# Distributed under the Boost Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#
+# Official repository: https://github.com/boostorg/beast
+#
+
+project
+ : requirements
+ ssl
+ crypto
+ ;
+
+exe http-client-async-ssl :
+ http_client_async_ssl.cpp
+ :
+ coverage:no
+ ubasan:no
+ ;
diff --git a/example/http/client/async-ssl/http_client_async_ssl.cpp b/example/http/client/async-ssl/http_client_async_ssl.cpp
new file mode 100644
index 00000000..5c39086f
--- /dev/null
+++ b/example/http/client/async-ssl/http_client_async_ssl.cpp
@@ -0,0 +1,212 @@
+//
+// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+//------------------------------------------------------------------------------
+//
+// Example: HTTP SSL client, asynchronous
+//
+//------------------------------------------------------------------------------
+
+#include "example/common/root_certificates.hpp"
+
+#include
+#include
+#include