diff --git a/CHANGELOG.md b/CHANGELOG.md
index d36417d7..cb72329b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+Version 218:
+
+* detect_ssl, async_detect_ssl are public interfaces
+
+--------------------------------------------------------------------------------
+
Version 217:
* websocket idle pings
diff --git a/doc/qbk/03_core/0_core.qbk b/doc/qbk/03_core/0_core.qbk
index 28cf9869..b52f0e7a 100644
--- a/doc/qbk/03_core/0_core.qbk
+++ b/doc/qbk/03_core/0_core.qbk
@@ -95,6 +95,5 @@ effect:
[include 4_buffers.qbk]
[include 5_files.qbk]
[include 6_composed.qbk]
-[include 7_detect_ssl.qbk]
[endsect]
diff --git a/doc/qbk/03_core/6_composed.qbk b/doc/qbk/03_core/6_composed.qbk
index 09e83980..f419f0a6 100644
--- a/doc/qbk/03_core/6_composed.qbk
+++ b/doc/qbk/03_core/6_composed.qbk
@@ -8,7 +8,6 @@
]
[section Writing Composed Operations]
-[block'''''']
Asynchronous operations are started by calling a free function or member
function known as an asynchronous ['__async_initfn__]. This function accepts
@@ -111,80 +110,7 @@ composed operations:
]]
]
-
-
-[section Echo]
-
-This example develops an initiating function called [*echo].
-The operation will read up to the first newline on a stream, and
-then write the same line including the newline back on the stream.
-First we define the input parameters and results, then declare our
-initiation function. For our echo operation the only inputs are the
-stream and the completion token. The output is the error code which
-is usually included in all completion handler signatures.
-
-[example_core_echo_op_2]
-
-Now that we have a declaration, we will define the body of the function.
-We want to achieve the following goals: perform static type checking on
-the input parameters, set up the return value as per __N3747__, and launch
-the composed operation by constructing an intermediate, stateful completion
-handler and invoking it.
-
-The initiating function contains a few relatively simple parts. There is
-the customization of the return value type, static type checking, building
-the return value type using the helper, and creating and launching the
-`echo_op` composed operation object.
-
-The implementation strategy is to make the composed object meet the
-requirements of a completion handler by being movable, and by making it
-invocable so it can be used as a continuation for the asynchronous operations
-it launches. Rather than using `std::bind` or `boost::bind`, which destroys
-the type information and therefore breaks the allocation and invocation hooks,
-we will simply pass `std::move(*this)` as the completion handler parameter for
-any operations that we initiate. For the move to work correctly, care must be
-taken to ensure that no access to data members are made after the move takes
-place. Here is the complete implementation of our composed operation:
-
-[example_core_echo_op_3]
-
-There are some common mistakes that should be avoided when writing
-composed operations:
-
-* Type erasing the final handler. This will cause undefined behavior.
-
-* Forgetting to include a return statement after calling an
- initiating function.
-
-* Calling a synchronous function by accident. In general composed
- operations should not block for long periods of time, since this
- ties up a thread running on the __io_context__.
-
-* Forgetting to provide `executor_type` and `get_executor` for the
- composed operation. This will cause undefined behavior. For example,
- if someone calls the initiating function with a strand-wrapped
- function object, and there is more than thread running on the
- __io_context__, the underlying stream may be accessed in a fashion
- that violates safety guarantees. Beast provides class templates
- to take care of this boilerplate for you.
-
-* Forgetting to create an object of type __executor_work_guard__ with the
- type of executor returned by the stream's `get_executor` member function.
-
-* For operations which complete immediately (i.e. without calling an
- intermediate initiating function), forgetting to use __post__ to
- invoke the final handler. This breaks the following initiating
- function guarantee: ['Regardless of whether the asynchronous operation
- completes immediately or not, the handler will not be invoked from
- within this function. Invocation of the handler will be performed
- in a manner equivalent to using __post__]. The function
- __bind_handler__ is provided for this purpose.
-
-A complete, runnable version of this example may be found in the examples
-directory.
-
-[endsect]
-
-
+[include 6a_echo.qbk]
+[include 6b_detect_ssl.qbk]
[endsect]
diff --git a/doc/qbk/03_core/6a_echo.qbk b/doc/qbk/03_core/6a_echo.qbk
new file mode 100644
index 00000000..e95be58b
--- /dev/null
+++ b/doc/qbk/03_core/6a_echo.qbk
@@ -0,0 +1,80 @@
+[/
+ 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
+]
+
+[section:echo Echo]
+
+This example develops an initiating function called [*echo].
+The operation will read up to the first newline on a stream, and
+then write the same line including the newline back on the stream.
+First we define the input parameters and results, then declare our
+initiation function. For our echo operation the only inputs are the
+stream and the completion token. The output is the error code which
+is usually included in all completion handler signatures.
+
+[example_core_echo_op_2]
+
+Now that we have a declaration, we will define the body of the function.
+We want to achieve the following goals: perform static type checking on
+the input parameters, set up the return value as per __N3747__, and launch
+the composed operation by constructing an intermediate, stateful completion
+handler and invoking it.
+
+The initiating function contains a few relatively simple parts. There is
+the customization of the return value type, static type checking, building
+the return value type using the helper, and creating and launching the
+`echo_op` composed operation object.
+
+The implementation strategy is to make the composed object meet the
+requirements of a completion handler by being movable, and by making it
+invocable so it can be used as a continuation for the asynchronous operations
+it launches. Rather than using `std::bind` or `boost::bind`, which destroys
+the type information and therefore breaks the allocation and invocation hooks,
+we will simply pass `std::move(*this)` as the completion handler parameter for
+any operations that we initiate. For the move to work correctly, care must be
+taken to ensure that no access to data members are made after the move takes
+place. Here is the complete implementation of our composed operation:
+
+[example_core_echo_op_3]
+
+There are some common mistakes that should be avoided when writing
+composed operations:
+
+* Type erasing the final handler. This will cause undefined behavior.
+
+* Forgetting to include a return statement after calling an
+ initiating function.
+
+* Calling a synchronous function by accident. In general composed
+ operations should not block for long periods of time, since this
+ ties up a thread running on the __io_context__.
+
+* Forgetting to provide `executor_type` and `get_executor` for the
+ composed operation. This will cause undefined behavior. For example,
+ if someone calls the initiating function with a strand-wrapped
+ function object, and there is more than thread running on the
+ __io_context__, the underlying stream may be accessed in a fashion
+ that violates safety guarantees. Beast provides class templates
+ to take care of this boilerplate for you.
+
+* Forgetting to create an object of type __executor_work_guard__ with the
+ type of executor returned by the stream's `get_executor` member function.
+
+* For operations which complete immediately (i.e. without calling an
+ intermediate initiating function), forgetting to use __post__ to
+ invoke the final handler. This breaks the following initiating
+ function guarantee: ['Regardless of whether the asynchronous operation
+ completes immediately or not, the handler will not be invoked from
+ within this function. Invocation of the handler will be performed
+ in a manner equivalent to using __post__]. The function
+ __bind_handler__ is provided for this purpose.
+
+A complete, runnable version of this example may be found in the examples
+directory.
+
+[endsect]
diff --git a/doc/qbk/03_core/6b_detect_ssl.qbk b/doc/qbk/03_core/6b_detect_ssl.qbk
new file mode 100644
index 00000000..ddf47693
--- /dev/null
+++ b/doc/qbk/03_core/6b_detect_ssl.qbk
@@ -0,0 +1,109 @@
+[/
+ 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
+]
+
+[section:detect_ssl Detect SSL]
+
+In this example we will build a simple function to detect the presence of the
+[@https://tools.ietf.org/html/rfc2246#section-7.4 TLS client handshake]
+given an input buffer sequence. Then we build on the example by adding a
+synchronous stream algorithm. Finally, we implement an asynchronous
+detection function using a composed operation. This SSL detector may
+be used to allow a server to accept both TLS and plain (unencrypted)
+connections at the same port.
+
+Here is the declaration for a function template to detect the SSL client
+handshake. The function accepts any object whose type meets the requirements
+of __ConstBufferSequence__. This gives callers flexibility to use a buffer
+object whose behavior is appropriate to the task.
+
+[example_core_detect_ssl_1]
+
+The algorithm examines the buffer starting from the beginning, and
+performs a series of qualifying checks against the TLS specification.
+When not enough data exists to be certain, the returned value of
+`boost::indeterminate` informs the caller to read more data into the buffer.
+The function definition for the declaration above follows:
+
+[example_core_detect_ssl_2]
+
+The detection function above is suitably generic and targeted in
+focus that it may be used as a building block to create higher level
+abstractions. Our goal is to create a ['stream algorithm]: a function
+which is invoked with a stream, that reads or writes (or both) to achieve
+a purpose. In this case, to detect the TLS client handshake. Stream
+algorithms may be synchronous or asynchronous. Because synchronous algorithms
+are easier to write, we start there. Then we build the asynchronous version,
+trying to model it similarly to make reasoning about it easier.
+
+The synchronous version is implemented thusly:
+
+[example_core_detect_ssl_3]
+
+Now that we have the synchronous version, we can attempt to model the
+asynchronous version similarly. A function which launches an asynchronous
+operation is called an ['initiating function]. While the synchronous
+version above produces an error code through an output parameter, the
+asynchronous version delivers the error code to a completion handler
+or other custom mechanism defined by the completion token. The signature
+of the initiating function reflects these differences.
+
+First we declare the initiating function and document the requirments,
+parameters, preconditions, and effects:
+
+[example_core_detect_ssl_4]
+
+There are two additional components required to implement the initiating
+function:
+
+* An intermediate completion handler, called the "composed operation"
+ object, which holds the state of the operation while it is in progress,
+ and also holds the user's completion handler to be invoked when the
+ opeartion completes, and
+
+* An "initiation" function object which when invoked with parameters
+ captured at the call site of the initiating function, constructs the
+ composed operation with the captured arguments and launches it.
+
+Here we forward declare the composed operation type, and provide the
+definition of the initiation function object. They are placed in the
+`detail` namespace since they should not be public:
+
+[example_core_detect_ssl_5]
+
+The initiating function definition itself is straightforward. We perform
+type checking on the parameters, and then let `net::async_initiate`
+capture the parameter list along with a copy of our initiation function
+object. Depending on the specialization of `async_result` for the type
+of `CompletionToken`, the initiation function may be invoked immediately.
+Alternatively, it may be invoked later, after the initiating function
+returns. This is known as "lazy execution," and allows efficient and
+expressive abstractions to be written.
+
+[example_core_detect_ssl_6]
+
+Now we will declare our composed operation. There is a considerable
+amount of necessary boilerplate to get this right, but the result
+is worth the effort.
+
+[example_core_detect_ssl_7]
+
+The boilerplate is all done, and now we need to implement the function
+call operator that turns this composed operation a completion handler
+with the signature `void(error_code, std::size_t)` which is exactly
+the signature needed when performing asynchronous reads. This function
+is a transformation of the synchronous version of `detect_ssl` above,
+but with the inversion of flow that characterizes code written in the
+callback style:
+
+[example_core_detect_ssl_8]
+
+This SSL detector is used in the advanced-flex and http-flex servers
+in the example directory.
+
+[endsect]
diff --git a/doc/qbk/03_core/7_detect_ssl.qbk b/doc/qbk/03_core/7_detect_ssl.qbk
deleted file mode 100644
index 595255d5..00000000
--- a/doc/qbk/03_core/7_detect_ssl.qbk
+++ /dev/null
@@ -1,69 +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
-]
-
-[section Example: Detect SSL]
-
-In this example we will build a simple function to detect the presence
-of the SSL handshake given an input buffer sequence. Then we build on
-the example by adding a synchronous stream algorithm. Finally, we
-implement an asynchronous detection function using a composed operation.
-This SSL detector may be used to allow a server to accept both SSL/TLS and
-unencrypted connections at the same port.
-
-Here is the declaration for a function to detect the SSL client handshake.
-The input to the function is simply a buffer sequence, no stream. This
-allows the detection algorithm to be used elsewhere.
-
-[example_core_detect_ssl_1]
-
-The implementation checks the buffer for the presence of the SSL
-Handshake message octet sequence and returns an apporopriate value:
-
-[example_core_detect_ssl_2]
-
-Now we define a stream operation. We start with the simple,
-synchronous version which takes the stream and buffer as input:
-
-[example_core_detect_ssl_3]
-
-The synchronous algorithm is the model for building the asynchronous
-operation which has more boilerplate. First, we declare the asynchronous
-initiating function:
-
-[example_core_detect_ssl_4]
-
-The implementation of the initiating function is straightforward
-and contains mostly boilerplate. It is to construct the return
-type customization helper to obtain the actual handler, and
-then create the composed operation and launch it. The actual
-code for interacting with the stream is in the composed operation,
-which is written as a separate class.
-
-[example_core_detect_ssl_5]
-
-Now we will declare our composed operation. There is a considerable
-amount of necessary boilerplate to get this right, but the result
-is worth the effort.
-
-[example_core_detect_ssl_6]
-
-The boilerplate is all done, and now we need to implement the function
-call operator that turns this composed operation a completion handler
-with the signature `void(error_code, std::size_t)` which is exactly
-the signature needed when performing asynchronous reads. This function
-is a transformation of the synchronous version of `detect_ssl` above,
-but with the inversion of flow that characterizes code written in the
-callback style:
-
-[example_core_detect_ssl_7]
-
-This SSL detector is used by the server framework in the example
-directory.
-
-[endsect]
diff --git a/doc/qbk/main.qbk b/doc/qbk/main.qbk
index f0bfe154..878cabc7 100644
--- a/doc/qbk/main.qbk
+++ b/doc/qbk/main.qbk
@@ -117,7 +117,6 @@
the paths close to absolute.
]
-[import ../../example/common/detect_ssl.hpp]
[import ../../example/doc/http_examples.hpp]
[import ../../example/echo-op/echo_op.cpp]
[import ../../example/http/client/sync/http_client_sync.cpp]
@@ -134,6 +133,7 @@
[import ../../test/doc/core_3_layers.cpp]
[import ../../test/doc/websocket_3_handshake.cpp]
+[import ../../include/boost/beast/core/detect_ssl.hpp]
[import ../../test/beast/core/rate_policy.cpp]
[section:quickref Reference]
diff --git a/doc/qbk/quickref.xml b/doc/qbk/quickref.xml
index b572d09c..7c55b58d 100644
--- a/doc/qbk/quickref.xml
+++ b/doc/qbk/quickref.xml
@@ -64,10 +64,12 @@
Functionsallocate_stable 🞲
+ async_detect_ssl 🞲beast_close_socket 🞲bind_front_handler 🞲bind_handlerclose_socket 🞲
+ detect_ssl 🞲generic_categoryget_lowest_layer 🞲iequals
diff --git a/doc/qbk/release_notes.qbk b/doc/qbk/release_notes.qbk
index a651713d..28060012 100644
--- a/doc/qbk/release_notes.qbk
+++ b/doc/qbk/release_notes.qbk
@@ -35,6 +35,13 @@
!
* Chat with us at the [*#beast] and [*#boost] channels in the
[@https://cppalliance.org/slack/ [*C++ Slack Workspace]].
+ * [role green [*More tutorials]], code like the pros!
+ * [link beast.using_io.asio_refresher Networking Refresher] teaches you from the ground up.
+ * Updated [link beast.using_io.writing_composed_operations.echo Asynchronous Echo] example
+ * Updated [link beast.using_io.writing_composed_operations.detect_ssl [*Detect SSL Handshake]],
+ now a [link beast.ref.boost__beast__async_detect_ssl public api]!
+ * [@../../example/websocket/server/chat-multi websocket-chat-multi]
+ threaded chat server with a JavaScript browser client.
* [link beast.ref.boost__beast__basic_stream `basic_stream`] and
[link beast.ref.boost__beast__basic_stream `tcp_stream`] offer:
* Timeouts:
@@ -47,8 +54,9 @@
[link beast.ref.boost__beast__unlimited_rate_policy `unlimited`],
or a user-defined
[link beast.concepts.RatePolicy ['RatePolicy]]!
- * [[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html P1322R0]]
- Put the strand on the socket itself, no more `bind_executor` at call sites!
+ * Put the strand directly on the socket using
+ [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html P1322R0],
+ no more `bind_executor` at call sites!
* Base classes
[link beast.ref.boost__beast__async_op_base `async_op_base`] and
[link beast.ref.boost__beast__stable_async_op_base `stable_async_op_base`]
@@ -56,10 +64,7 @@
* All asynchronous operations use Asio's
[@boost:/doc/html/boost_asio/reference/async_initiate.html `async_initiate`]
for efficient integration with Coroutines TS.
- * New [@../../example/websocket/server/chat-multi websocket-chat-multi]
- multi-threaded websocket chat server with a JavaScript browser client.
- * New [link beast.using_io.asio_refresher Networking Refresher] explains basic concepts.
- * OpenSSL is required to build tests and examples
+ * OpenSSL is now required to build tests and examples
* See the full [link beast.release_notes [*Release Notes]] for a complete list
of changes.
diff --git a/example/advanced/server-flex/CMakeLists.txt b/example/advanced/server-flex/CMakeLists.txt
index 094f9acb..15c92b4b 100644
--- a/example/advanced/server-flex/CMakeLists.txt
+++ b/example/advanced/server-flex/CMakeLists.txt
@@ -14,7 +14,6 @@ if (OPENSSL_FOUND)
add_executable (advanced-server-flex
${BOOST_BEAST_FILES}
- ${PROJECT_SOURCE_DIR}/example/common/detect_ssl.hpp
${PROJECT_SOURCE_DIR}/example/common/server_certificate.hpp
Jamfile
advanced_server_flex.cpp
diff --git a/example/advanced/server-flex/advanced_server_flex.cpp b/example/advanced/server-flex/advanced_server_flex.cpp
index f51f7ba0..dcb3c627 100644
--- a/example/advanced/server-flex/advanced_server_flex.cpp
+++ b/example/advanced/server-flex/advanced_server_flex.cpp
@@ -13,7 +13,6 @@
//
//------------------------------------------------------------------------------
-#include "example/common/detect_ssl.hpp"
#include "example/common/server_certificate.hpp"
#include
@@ -822,7 +821,7 @@ public:
// Set the timeout.
stream_.expires_after(std::chrono::seconds(30));
- async_detect_ssl(
+ beast::async_detect_ssl(
stream_,
buffer_,
beast::bind_front_handler(
diff --git a/example/common/detect_ssl.hpp b/example/common/detect_ssl.hpp
deleted file mode 100644
index a941d7aa..00000000
--- a/example/common/detect_ssl.hpp
+++ /dev/null
@@ -1,476 +0,0 @@
-//
-// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/boostorg/beast
-//
-
-#ifndef BOOST_BEAST_EXAMPLE_COMMON_DETECT_SSL_HPP
-#define BOOST_BEAST_EXAMPLE_COMMON_DETECT_SSL_HPP
-
-#include
-#include
-
-//------------------------------------------------------------------------------
-//
-// Example: Detect TLS/SSL
-//
-//------------------------------------------------------------------------------
-
-//[example_core_detect_ssl_1
-
-#include
-#include
-#include
-#include
-
-/** Return `true` if a buffer contains a TLS/SSL client handshake.
-
- This function returns `true` if the beginning of the buffer
- indicates that a TLS handshake is being negotiated, and that
- there are at least four octets in the buffer.
-
- If the content of the buffer cannot possibly be a TLS handshake
- request, the function returns `false`. Otherwise, if additional
- octets are required, `boost::indeterminate` is returned.
-
- @param buffer The input buffer to inspect. This type must meet
- the requirements of @b ConstBufferSequence.
-
- @return `boost::tribool` indicating whether the buffer contains
- a TLS client handshake, does not contain a handshake, or needs
- additional octets.
-
- @see
-
- http://www.ietf.org/rfc/rfc2246.txt
- 7.4. Handshake protocol
-*/
-template
-boost::tribool
-is_ssl_handshake(ConstBufferSequence const& buffers);
-
-//]
-
-//[example_core_detect_ssl_2
-
-template<
- class ConstBufferSequence>
-boost::tribool
-is_ssl_handshake(
- ConstBufferSequence const& buffers)
-{
- // Make sure buffers meets the requirements
- static_assert(
- boost::asio::is_const_buffer_sequence::value,
- "ConstBufferSequence type requirements not met");
-
- // Extract the first byte, which holds the
- // "message" type for the Handshake protocol.
- unsigned char v;
- if(boost::asio::buffer_copy(boost::asio::buffer(&v, 1), buffers) < 1)
- {
- // We need at least one byte to really do anything
- return boost::indeterminate;
- }
-
- // Check that the message type is "SSL Handshake" (rfc2246)
- if(v != 0x16)
- {
- // This is definitely not a handshake
- return false;
- }
-
- // At least four bytes are needed for the handshake
- // so make sure that we get them before returning `true`
- if(boost::asio::buffer_size(buffers) < 4)
- return boost::indeterminate;
-
- // This can only be a TLS/SSL handshake
- return true;
-}
-
-//]
-
-//[example_core_detect_ssl_3
-
-/** Detect a TLS/SSL handshake on a stream.
-
- This function reads from a stream to determine if a TLS/SSL
- handshake is being received. The function call will block
- until one of the following conditions is true:
-
- @li The disposition of the handshake is determined
-
- @li An error occurs
-
- Octets read from the stream will be stored in the passed dynamic
- buffer, which may be used to perform the TLS handshake if the
- detector returns true, or otherwise consumed by the caller based
- on the expected protocol.
-
- @param stream The stream to read from. This type must meet the
- requirements of @b SyncReadStream.
-
- @param buffer The dynamic buffer to use. This type must meet the
- requirements of @b DynamicBuffer.
-
- @param ec Set to the error if any occurred.
-
- @return `boost::tribool` indicating whether the buffer contains
- a TLS client handshake, does not contain a handshake, or needs
- additional octets. If an error occurs, the return value is
- undefined.
-*/
-template<
- class SyncReadStream,
- class DynamicBuffer>
-boost::tribool
-detect_ssl(
- SyncReadStream& stream,
- DynamicBuffer& buffer,
- boost::beast::error_code& ec)
-{
- namespace beast = boost::beast;
-
- // Make sure arguments meet the requirements
- static_assert(beast::is_sync_read_stream::value,
- "SyncReadStream type requirements not met");
- static_assert(
- boost::asio::is_dynamic_buffer::value,
- "DynamicBuffer type requirements not met");
-
- // Loop until an error occurs or we get a definitive answer
- for(;;)
- {
- // There could already be data in the buffer
- // so we do this first, before reading from the stream.
- auto const result = is_ssl_handshake(buffer.data());
-
- // If we got an answer, return it
- if(! boost::indeterminate(result))
- {
- // This is a fast way to indicate success
- // without retrieving the default category.
- ec = {};
- return result;
- }
-
- // The algorithm should never need more than 4 bytes
- BOOST_ASSERT(buffer.size() < 4);
-
- // 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);
-
- // Check for an error
- if(ec)
- break;
-
- // Commit what we read into the buffer's input area.
- buffer.commit(bytes_transferred);
- }
-
- // error
- return false;
-}
-
-//]
-
-//[example_core_detect_ssl_4
-
-/** Detect a TLS/SSL handshake asynchronously on a stream.
-
- This function is used to asynchronously determine if a TLS/SSL
- handshake is being received.
- The function call always returns immediately. The asynchronous
- operation will continue until one of the following conditions
- is true:
-
- @li The disposition of the handshake is determined
-
- @li An error occurs
-
- This operation is implemented in terms of zero or more calls to
- the next layer's `async_read_some` function, and is known as a
- composed operation. The program must ensure that the
- stream performs no other operations until this operation completes.
-
- Octets read from the stream will be stored in the passed dynamic
- buffer, which may be used to perform the TLS handshake if the
- detector returns true, or otherwise consumed by the caller based
- on the expected protocol.
-
- @param stream The stream to read from. This type must meet the
- requirements of @b AsyncReadStream.
-
- @param buffer The dynamic buffer to use. This type must meet the
- requirements of @b DynamicBuffer.
-
- @param handler The completion handler to invoke when the operation
- completes. The implementation takes ownership of the handler by
- performing a decay-copy. The equivalent function signature of
- the handler must be:
-
- @code
- void handler(
- error_code const& error, // Set to the error, if any
- boost::tribool result // The result of the detector
- );
- @endcode
- Regardless of whether the asynchronous operation completes
- immediately or not, the handler will not be invoked from within
- this function. Invocation of the handler will be performed in a
- manner equivalent to using `net::post`.
-*/
-template<
- class AsyncReadStream,
- class DynamicBuffer,
- class CompletionToken>
-BOOST_ASIO_INITFN_RESULT_TYPE( /*< `BOOST_ASIO_INITFN_RESULT_TYPE` customizes the return value based on the completion token >*/
- CompletionToken,
- void(boost::beast::error_code, boost::tribool)) /*< This is the signature for the completion handler >*/
-async_detect_ssl(
- AsyncReadStream& stream,
- DynamicBuffer& buffer,
- CompletionToken&& token);
-
-//]
-
-//[example_core_detect_ssl_5
-
-// This is the composed operation.
-template<
- class AsyncReadStream,
- class DynamicBuffer,
- class Handler>
-class detect_ssl_op;
-
-// Here is the implementation of the asynchronous initiation function
-template<
- class AsyncReadStream,
- class DynamicBuffer,
- class CompletionToken>
-BOOST_ASIO_INITFN_RESULT_TYPE(
- CompletionToken,
- 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(beast::is_async_read_stream::value,
- "SyncReadStream type requirements not met");
- static_assert(
- boost::asio::is_dynamic_buffer::value,
- "DynamicBuffer type 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::asio::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 BOOST_ASIO_HANDLER_TYPE
- // to convert the completion token into the correct handler type,
- // allowing user defined specializations of the async result template
- // to take effect.
- //
- detect_ssl_op<
- AsyncReadStream,
- DynamicBuffer,
- BOOST_ASIO_HANDLER_TYPE(
- CompletionToken, void(beast::error_code, boost::tribool))>(
- stream, buffer, std::move(init.completion_handler))(beast::error_code{}, 0);
-
- // This hook lets the caller see a return value when appropriate.
- // For example this might return std::future if
- // CompletionToken is boost::asio::use_future.
- //
- // If a coroutine is used for the token, the return value from
- // this function will be the `boost::tribool` representing the result.
- //
- return init.result.get();
-}
-
-//]
-
-//[example_core_detect_ssl_6
-
-// Read from a stream to invoke is_tls_handshake asynchronously.
-// This will be implemented using Asio's "stackless coroutines"
-// which are based on macros forming a switch statement. The
-// operation is derived from `coroutine` for this reason.
-//
-template<
- class AsyncReadStream,
- class DynamicBuffer,
- class Handler>
-class detect_ssl_op : public boost::asio::coroutine
-{
- // This composed operation has trivial state,
- // so it is just kept inside the class and can
- // be cheaply copied as needed by the implementation.
-
- AsyncReadStream& stream_;
-
- // Boost.Asio and the Networking TS require an object of
- // type executor_work_guard, where T is the type of
- // executor returned by the stream's get_executor function,
- // to persist for the duration of the asynchronous operation.
- boost::asio::executor_work_guard<
- decltype(std::declval().get_executor())> work_;
-
- DynamicBuffer& buffer_;
- Handler handler_;
- boost::tribool result_ = false;
-
-public:
- // Boost.Asio requires that handlers are CopyConstructible.
- // The state for this operation is cheap to copy.
- detect_ssl_op(detect_ssl_op const&) = default;
-
- // The constructor just keeps references the callers variables.
- //
- template
- detect_ssl_op(
- AsyncReadStream& stream,
- DynamicBuffer& buffer,
- DeducedHandler&& handler)
- : stream_(stream)
- , work_(stream.get_executor())
- , buffer_(buffer)
- , handler_(std::forward(handler))
- {
- }
-
- // Associated allocator support. This is Asio's system for
- // allowing the final completion handler to customize the
- // memory allocation strategy used for composed operation
- // states. A composed operation needs to use the same allocator
- // as the final handler. These declarations achieve that.
-
- using allocator_type =
- boost::asio::associated_allocator_t;
-
- allocator_type
- get_allocator() const noexcept
- {
- return (boost::asio::get_associated_allocator)(handler_);
- }
-
- // Executor hook. This is Asio's system for customizing the
- // manner in which asynchronous completion handlers are invoked.
- // A composed operation needs to use the same executor to invoke
- // intermediate completion handlers as that used to invoke the
- // final handler.
-
- using executor_type = boost::asio::associated_executor_t<
- Handler, decltype(std::declval().get_executor())>;
-
- executor_type get_executor() const noexcept
- {
- return (boost::asio::get_associated_executor)(handler_, stream_.get_executor());
- }
-
- // Our main entry point. This will get called as our
- // intermediate operations complete. Definition below.
- //
- void operator()(boost::beast::error_code ec, std::size_t bytes_transferred);
-};
-
-//]
-
-//[example_core_detect_ssl_7
-
-// detect_ssl_op is callable with the signature
-// void(error_code, bytes_transferred),
-// allowing `*this` to be used as a ReadHandler
-//
-template<
- class AsyncStream,
- class DynamicBuffer,
- class Handler>
-void
-detect_ssl_op::
-operator()(boost::beast::error_code ec, std::size_t bytes_transferred)
-{
- namespace beast = boost::beast;
-
- // This introduces the scope of the stackless coroutine
- BOOST_ASIO_CORO_REENTER(*this)
- {
- // There could already be data in the buffer
- // so we do this first, before reading from the stream.
- result_ = is_ssl_handshake(buffer_.data());
-
- // If we got an answer, return it
- if(! boost::indeterminate(result_))
- {
- // We need to invoke the handler, but the guarantee
- // is that the handler will not be called before the
- // call to async_detect_ssl returns, so we must post
- // the operation to the executor. The helper function
- // `bind_handler` lets us bind arguments in a safe way
- // that preserves the type customization hooks of the
- // original handler.
- BOOST_ASIO_CORO_YIELD
- boost::asio::post(
- stream_.get_executor(),
- beast::bind_front_handler(
- std::move(*this), ec, 0));
- }
- else
- {
- // Loop until an error occurs or we get a definitive answer
- for(;;)
- {
- // The algorithm should never need more than 4 bytes
- BOOST_ASSERT(buffer_.size() < 4);
-
- BOOST_ASIO_CORO_YIELD
- {
- // 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
- stream_.async_read_some(mutable_buffer, std::move(*this));
- }
-
- // Check for an error
- if(ec)
- break;
-
- // Commit what we read into the buffer's input area.
- buffer_.commit(bytes_transferred);
-
- // See if we can detect the handshake
- result_ = is_ssl_handshake(buffer_.data());
-
- // If it is detected, call the handler
- if(! boost::indeterminate(result_))
- {
- // We don't need bind_handler here because we were invoked
- // as a result of an intermediate asynchronous operation.
- break;
- }
- }
- }
-
- // Invoke the final handler.
- handler_(ec, result_);
- }
-}
-
-//]
-
-#endif
diff --git a/example/echo-op/echo_op.cpp b/example/echo-op/echo_op.cpp
index ee33d0b2..b91dbd5e 100644
--- a/example/echo-op/echo_op.cpp
+++ b/example/echo-op/echo_op.cpp
@@ -101,6 +101,11 @@ async_echo (
template
class echo_op;
+// This example uses the Asio's stackless "fauxroutines", implemented
+// using a macro-based solution. It makes the code easier to write and
+// easier to read. This include file defines the necessary macros and types.
+#include
+
// Read a line and echo it back
//
template<
@@ -153,12 +158,6 @@ async_echo(
beast::executor_type /*< The type of executor used by the stream to dispatch asynchronous operations >*/
>;
- // This example uses the Asio's stackless "fauxroutines", implemented
- // using a macro-based solution. It makes the code easier to write and
- // easier to read. This include file defines the necessary macros and types.
-
-#include
-
// This nested class implements the echo composed operation as a
// stateful completion handler. We derive from `async_op_base` to
// take care of boilerplate and we derived from net::coroutine to
@@ -313,9 +312,6 @@ async_echo(
}
};
-// Including this file undefines the macros used by the stackless fauxroutines.
-#include
-
// Create the composed operation and launch it. This is a constructor
// call followed by invocation of operator(). We use BOOST_ASIO_HANDLER_TYPE
// to convert the completion token into the correct handler type,
@@ -332,6 +328,9 @@ async_echo(
return init.result.get();
}
+// Including this file undefines the macros used by the stackless fauxroutines.
+#include
+
//]
struct move_only_handler
diff --git a/example/http/server/flex/CMakeLists.txt b/example/http/server/flex/CMakeLists.txt
index f14d0379..46bf8498 100644
--- a/example/http/server/flex/CMakeLists.txt
+++ b/example/http/server/flex/CMakeLists.txt
@@ -14,7 +14,6 @@ if (OPENSSL_FOUND)
add_executable (http-server-flex
${BOOST_BEAST_FILES}
- ${PROJECT_SOURCE_DIR}/example/common/detect_ssl.hpp
${PROJECT_SOURCE_DIR}/example/common/server_certificate.hpp
Jamfile
http_server_flex.cpp
diff --git a/example/http/server/flex/http_server_flex.cpp b/example/http/server/flex/http_server_flex.cpp
index 486fab78..d986fdbe 100644
--- a/example/http/server/flex/http_server_flex.cpp
+++ b/example/http/server/flex/http_server_flex.cpp
@@ -13,7 +13,6 @@
//
//------------------------------------------------------------------------------
-#include "example/common/detect_ssl.hpp"
#include "example/common/server_certificate.hpp"
#include
diff --git a/include/boost/beast/_experimental/test/handler.hpp b/include/boost/beast/_experimental/test/handler.hpp
index 2f8af629..ccafa950 100644
--- a/include/boost/beast/_experimental/test/handler.hpp
+++ b/include/boost/beast/_experimental/test/handler.hpp
@@ -12,6 +12,7 @@
#include
#include
+#include
#include
#include
@@ -140,6 +141,46 @@ fail_handler(error_code ec) noexcept
return handler(ec);
}
+/** Run an I/O context.
+
+ This function runs and dispatches handlers on the specified
+ I/O context, until one of the following conditions is true:
+
+ @li The I/O context runs out of work.
+
+ @param ioc The I/O context to run
+*/
+inline
+void
+run(net::io_context& ioc)
+{
+ ioc.run();
+ ioc.restart();
+}
+
+/** Run an I/O context for a certain amount of time.
+
+ This function runs and dispatches handlers on the specified
+ I/O context, until one of the following conditions is true:
+
+ @li The I/O context runs out of work.
+
+ @li No completions occur and the specified amount of time has elapsed.
+
+ @param ioc The I/O context to run
+
+ @param elapsed The maximum amount of time to run for.
+*/
+template
+void
+run_for(
+ net::io_context& ioc,
+ std::chrono::duration elapsed)
+{
+ ioc.run_for(elapsed);
+ ioc.restart();
+}
+
} // test
} // beast
} // boost
diff --git a/include/boost/beast/_experimental/test/tcp.hpp b/include/boost/beast/_experimental/test/tcp.hpp
index dc54673d..d794b4ee 100644
--- a/include/boost/beast/_experimental/test/tcp.hpp
+++ b/include/boost/beast/_experimental/test/tcp.hpp
@@ -21,46 +21,6 @@ namespace boost {
namespace beast {
namespace test {
-/** Run an I/O context.
-
- This function runs and dispatches handlers on the specified
- I/O context, until one of the following conditions is true:
-
- @li The I/O context runs out of work.
-
- @param ioc The I/O context to run
-*/
-inline
-void
-run(net::io_context& ioc)
-{
- ioc.run();
- ioc.restart();
-}
-
-/** Run an I/O context for a certain amount of time.
-
- This function runs and dispatches handlers on the specified
- I/O context, until one of the following conditions is true:
-
- @li The I/O context runs out of work.
-
- @li No completions occur and the specified amount of time has elapsed.
-
- @param ioc The I/O context to run
-
- @param elapsed The maximum amount of time to run for.
-*/
-template
-void
-run_for(
- net::io_context& ioc,
- std::chrono::duration elapsed)
-{
- ioc.run_for(elapsed);
- ioc.restart();
-}
-
/** Connect two TCP sockets together.
*/
template
diff --git a/include/boost/beast/core.hpp b/include/boost/beast/core.hpp
index e0dc65dc..51c9988d 100644
--- a/include/boost/beast/core.hpp
+++ b/include/boost/beast/core.hpp
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/include/boost/beast/core/detect_ssl.hpp b/include/boost/beast/core/detect_ssl.hpp
new file mode 100644
index 00000000..98aad395
--- /dev/null
+++ b/include/boost/beast/core/detect_ssl.hpp
@@ -0,0 +1,612 @@
+//
+// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_CORE_DETECT_SSL_HPP
+#define BOOST_BEAST_CORE_DETECT_SSL_HPP
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace boost {
+namespace beast {
+
+//------------------------------------------------------------------------------
+//
+// Example: Detect TLS client_hello
+//
+// This is an example and also a public interface. It implements
+// an algorithm for determining if a "TLS client_hello" message
+// is received. It can be used to implement a listening port that
+// can handle both plain and TLS encrypted connections.
+//
+//------------------------------------------------------------------------------
+
+//[example_core_detect_ssl_1
+
+// By convention, the "detail" namespace means "not-public."
+// Identifiers in a detail namespace are not visible in the documentation,
+// and users should not directly use those identifiers in programs, otherwise
+// their program may break in the future.
+//
+// Using a detail namespace gives the library writer the freedom to change
+// the interface or behavior later, and maintain backward-compatibility.
+
+namespace detail {
+
+/** Return `true` if the buffer contains a TLS Protocol client_hello message.
+
+ This function analyzes the bytes at the beginning of the buffer
+ and compares it to a valid client_hello message. This is the
+ message required to be sent by a client at the beginning of
+ any TLS (encrypted communication) session, including when
+ resuming a session.
+
+ The return value will be:
+
+ @li `true` if the contents of the buffer unambiguously define
+ contain a client_hello message,
+
+ @li `false` if the contents of the buffer cannot possibly
+ be a valid client_hello message, or
+
+ @li `boost::indeterminate` if the buffer contains an
+ insufficient number of bytes to determine the result. In
+ this case the caller should read more data from the relevant
+ stream, append it to the buffers, and call this function again.
+
+ @param buffers The buffer sequence to inspect.
+ This type must meet the requirements of ConstBufferSequence.
+
+ @return `boost::tribool` indicating whether the buffer contains
+ a TLS client handshake, does not contain a handshake, or needs
+ additional bytes to determine an outcome.
+
+ @see
+
+ 7.4. Handshake protocol
+ (RFC2246: The TLS Protocol)
+*/
+template
+boost::tribool
+is_tls_client_hello (ConstBufferSequence const& buffers);
+
+} // detail
+
+//]
+
+//[example_core_detect_ssl_2
+
+namespace detail {
+
+template
+boost::tribool
+is_tls_client_hello (ConstBufferSequence const& buffers)
+{
+ // Make sure buffers meets the requirements
+ static_assert(
+ net::is_const_buffer_sequence::value,
+ "ConstBufferSequence type requirements not met");
+
+/*
+ The first message on a TLS connection must be the client_hello,
+ which is a type of handshake record, and it cannot be compressed
+ or encrypted. A plaintext record has this format:
+
+ 0 byte record_type // 0x16 = handshake
+ 1 byte major // major protocol version
+ 2 byte minor // minor protocol version
+ 3-4 uint16 length // size of the payload
+ 5 byte handshake_type // 0x01 = client_hello
+ 6 uint24 length // size of the ClientHello
+ 9 byte major // major protocol version
+ 10 byte minor // minor protocol version
+ 11 uint32 gmt_unix_time
+ 15 byte random_bytes[28]
+ ...
+*/
+
+ // Flatten the input buffers into a single contiguous range
+ // of bytes on the stack to make it easier to work with the data.
+ unsigned char buf[9];
+ auto const n = net::buffer_copy(
+ net::mutable_buffer(buf, sizeof(buf)), buffers);
+
+ // Can't do much without any bytes
+ if(n < 1)
+ return boost::indeterminate;
+
+ // Require the first byte to be 0x16, indicating a TLS handshake record
+ if(buf[0] != 0x16)
+ return false;
+
+ // We need at least 5 bytes to know the record payload size
+ if(n < 5)
+ return boost::indeterminate;
+
+ // Calculate the record payload size
+ std::uint32_t const length = (buf[3] << 8) + buf[4];
+
+ // A ClientHello message payload is at least 34 bytes.
+ // There can be multiple handshake messages in the same record.
+ if(length < 34)
+ return false;
+
+ // We need at least 6 bytes to know the handshake type
+ if(n < 6)
+ return boost::indeterminate;
+
+ // The handshake_type must be 0x01 == client_hello
+ if(buf[5] != 0x01)
+ return false;
+
+ // We need at least 9 bytes to know the payload size
+ if(n < 9)
+ return boost::indeterminate;
+
+ // Calculate the message payload size
+ std::uint32_t const size =
+ (buf[6] << 16) + (buf[7] << 8) + buf[8];
+
+ // The message payload can't be bigger than the enclosing record
+ if(size + 4 > length)
+ return false;
+
+ // This can only be a TLS client_hello message
+ return true;
+}
+
+} // detail
+
+//]
+
+//[example_core_detect_ssl_3
+
+/** Detect a TLS client handshake on a stream.
+
+ This function reads from a stream to determine if a client
+ handshake message is being received.
+
+ The call blocks until one of the following is true:
+
+ @li A TLS client opening handshake is detected,
+
+ @li The received data is invalid for a TLS client handshake, or
+
+ @li An error occurs.
+
+ The algorithm, known as a composed operation, is implemented
+ in terms of calls to the next layer's `read_some` function.
+
+ Bytes read from the stream will be stored in the passed dynamic
+ buffer, which may be used to perform the TLS handshake if the
+ detector returns true, or be otherwise consumed by the caller based
+ on the expected protocol.
+
+ @param stream The stream to read from. This type must meet the
+ requirements of SyncReadStream.
+
+ @param buffer The dynamic buffer to use. This type must meet the
+ requirements of DynamicBuffer.
+
+ @param ec Set to the error if any occurred.
+
+ @return `boost::tribool` indicating whether the buffer contains
+ a TLS client handshake, does not contain a handshake, or needs
+ additional octets. If an error occurs, the return value is
+ undefined.
+*/
+template<
+ class SyncReadStream,
+ class DynamicBuffer>
+boost::tribool
+detect_ssl(
+ SyncReadStream& stream,
+ DynamicBuffer& buffer,
+ error_code& ec)
+{
+ namespace beast = boost::beast;
+
+ // Make sure arguments meet the requirements
+
+ static_assert(
+ is_sync_read_stream::value,
+ "SyncReadStream type requirements not met");
+
+ static_assert(
+ net::is_dynamic_buffer::value,
+ "DynamicBuffer type requirements not met");
+
+ // Loop until an error occurs or we get a definitive answer
+ for(;;)
+ {
+ // There could already be data in the buffer
+ // so we do this first, before reading from the stream.
+ auto const result = detail::is_tls_client_hello(buffer.data());
+
+ // If we got an answer, return it
+ if(! boost::indeterminate(result))
+ {
+ // A definite answer is a success
+ ec = {};
+ return result;
+ }
+
+ // Try to fill our buffer by reading from the stream.
+ // The function read_size calculates a reasonable size for the
+ // amount to read next, using existing capacity if possible to
+ // avoid allocating memory, up to the limit of 1536 bytes which
+ // is the size of a normal TCP frame.
+
+ std::size_t const bytes_transferred = stream.read_some(
+ buffer.prepare(beast::read_size(buffer, 1536)), ec);
+
+ // Commit what we read into the buffer's input area.
+ buffer.commit(bytes_transferred);
+
+ // Check for an error
+ if(ec)
+ break;
+ }
+
+ // error
+ return false;
+}
+
+//]
+
+//[example_core_detect_ssl_4
+
+/** Detect a TLS/SSL handshake asynchronously on a stream.
+
+ This function reads asynchronously from a stream to determine
+ if a client handshake message is being received.
+
+ This call always returns immediately. The asynchronous operation
+ will continue until one of the following conditions is true:
+
+ @li A TLS client opening handshake is detected,
+
+ @li The received data is invalid for a TLS client handshake, or
+
+ @li An error occurs.
+
+ The algorithm, known as a composed asynchronous operation,
+ is implemented in terms of calls to the next layer's `async_read_some`
+ function. The program must ensure that no other calls to
+ `async_read_some` are performed until this operation completes.
+
+ Bytes read from the stream will be stored in the passed dynamic
+ buffer, which may be used to perform the TLS handshake if the
+ detector returns true, or be otherwise consumed by the caller based
+ on the expected protocol.
+
+ @param stream The stream to read from. This type must meet the
+ requirements of AsyncReadStream.
+
+ @param buffer The dynamic buffer to use. This type must meet the
+ requirements of DynamicBuffer.
+
+ @param token The completion token used to determine the method
+ used to provide the result of the asynchronous operation. If
+ this is a completion handler, the implementation takes ownership
+ of the handler by performing a decay-copy, and the equivalent
+ function signature of the handler must be:
+
+ @code
+ void handler(
+ error_code const& error, // Set to the error, if any
+ boost::tribool result // The result of the detector
+ );
+ @endcode
+
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `net::post`.
+*/
+template<
+ class AsyncReadStream,
+ class DynamicBuffer,
+ class CompletionToken>
+auto
+async_detect_ssl(
+ AsyncReadStream& stream,
+ DynamicBuffer& buffer,
+ CompletionToken&& token)
+ -> typename net::async_result<
+ typename std::decay::type, /*< `async_result` customizes the return value based on the completion token >*/
+ void(error_code, boost::tribool)>::return_type; /*< This is the signature for the completion handler >*/
+//]
+
+//[example_core_detect_ssl_5
+
+// These implementation details don't need to be public
+
+namespace detail {
+
+// The composed operation object
+template<
+ class DetectHandler,
+ class AsyncReadStream,
+ class DynamicBuffer>
+class detect_ssl_op;
+
+// This is a function object which `net::async_initiate` can use to launch
+// our composed operation. This is a relatively new feature in networking
+// which allows the asynchronous operation to be "lazily" executed (meaning
+// that it is launched later). Users don't need to worry about this, but
+// authors of composed operations need to write it this way to get the
+// very best performance, for example when using Coroutines TS (`co_await`).
+
+struct run_detect_ssl_op
+{
+ // The implementation of `net::async_initiate` captures the
+ // arguments of the initiating function, and then calls this
+ // function object later with the captured arguments in order
+ // to launch the composed operation. All we need to do here
+ // is take those arguments and construct our composed operation
+ // object.
+ //
+ // `async_initiate` takes care of transforming the completion
+ // token into the "real handler" which must have the correct
+ // signature, in this case `void(error_code, boost::tri_bool)`.
+
+ template<
+ class DetectHandler,
+ class AsyncReadStream,
+ class DynamicBuffer>
+ void operator()(
+ DetectHandler&& h,
+ AsyncReadStream& s,
+ DynamicBuffer& b)
+ {
+ detect_ssl_op<
+ typename std::decay::type,
+ AsyncReadStream,
+ DynamicBuffer>(
+ std::forward(h),
+ s,
+ b);
+ }
+};
+
+} // detail
+
+//]
+
+//[example_core_detect_ssl_6
+
+// Here is the implementation of the asynchronous initiation function
+template<
+ class AsyncReadStream,
+ class DynamicBuffer,
+ class CompletionToken>
+auto
+async_detect_ssl(
+ AsyncReadStream& stream,
+ DynamicBuffer& buffer,
+ CompletionToken&& token)
+ -> typename net::async_result<
+ typename std::decay::type,
+ void(error_code, boost::tribool)>::return_type
+{
+ // Make sure arguments meet the type requirements
+
+ static_assert(
+ is_async_read_stream::value,
+ "SyncReadStream type requirements not met");
+
+ static_assert(
+ net::is_dynamic_buffer::value,
+ "DynamicBuffer type requirements not met");
+
+ // The function `net::async_initate` uses customization points
+ // to allow one asynchronous initiating function to work with
+ // all sorts of notification systems, such as callbacks but also
+ // fibers, futures, coroutines, and user-defined types.
+ //
+ // It works by capturing all of the arguments using perfect
+ // forwarding, and then depending on the specialization of
+ // `net::async_result` for the type of `CompletionToken`,
+ // the `initiation` object will be invoked with the saved
+ // parameters and the actual completion handler. Our
+ // initiating object is `run_detect_ssl_op`
+
+ return net::async_initiate<
+ CompletionToken,
+ void(error_code, boost::tribool)>(
+ detail::run_detect_ssl_op{},
+ token,
+ stream,
+ buffer);
+}
+
+//]
+
+//[example_core_detect_ssl_7
+
+namespace detail {
+
+// Read from a stream, calling is_tls_client_hello on the data
+// data to determine if the TLS client handshake is present.
+//
+// This will be implemented using Asio's "stackless coroutines"
+// which are based on macros forming a switch statement. The
+// operation is derived from `coroutine` for this reason.
+//
+// The library type `async_op_base` takes care of all of the
+// boilerplate for writing composed operations, including:
+//
+// * Storing the user's completion handler
+// * Maintaining the work guard for the handler's associated executor
+// * Propagating the associated allocator of the handler
+// * Propagating the associated executor of the handler
+// * Deallocating temporary storage before invoking the handler
+// * Posting the handler to the executor on an immediate completion
+//
+// `async_op_base` needs to know the type of the handler, as well
+// as the executor of the I/O object being used. The metafunction
+// `executor_type` returns the type of executor used by an
+// I/O object.
+//
+template<
+ class DetectHandler,
+ class AsyncReadStream,
+ class DynamicBuffer>
+class detect_ssl_op
+ : public boost::asio::coroutine
+ , public async_op_base<
+ DetectHandler, executor_type>
+{
+ // This composed operation has trivial state,
+ // so it is just kept inside the class and can
+ // be cheaply copied as needed by the implementation.
+
+ AsyncReadStream& stream_;
+
+ // The callers buffer is used to hold all received data
+ DynamicBuffer& buffer_;
+
+ boost::tribool result_ = false;
+
+public:
+ // Completion handlers must be MoveConstructible.
+ detect_ssl_op(detect_ssl_op&&) = default;
+
+ // Construct the operation. The handler is deduced through
+ // the template type `DetectHandler_`, this lets the same constructor
+ // work properly for both lvalues and rvalues.
+ //
+ template
+ detect_ssl_op(
+ DetectHandler_&& handler,
+ AsyncReadStream& stream,
+ DynamicBuffer& buffer)
+ : async_op_base>(
+ std::forward(handler),
+ stream.get_executor())
+ , stream_(stream)
+ , buffer_(buffer)
+ {
+ // This starts the operation. We pass `false` to tell the
+ // algorithm that it needs to use net::post if it wants to
+ // complete immediately. This is required by Networking,
+ // as initiating functions are not allowed to invoke the
+ // completion handler on the caller's thread before
+ // returning.
+ (*this)({}, 0, false);
+ }
+
+ // Our main entry point. This will get called as our
+ // intermediate operations complete. Definition below.
+ //
+ // The parameter `cont` indicates if we are being called subsequently
+ // from the original invocation
+ //
+ void operator()(
+ error_code ec,
+ std::size_t bytes_transferred,
+ bool cont = true);
+};
+
+} // detail
+
+//]
+
+//[example_core_detect_ssl_8
+
+namespace detail {
+
+// This example uses the Asio's stackless "fauxroutines", implemented
+// using a macro-based solution. It makes the code easier to write and
+// easier to read. This include file defines the necessary macros and types.
+#include
+
+// detect_ssl_op is callable with the signature void(error_code, bytes_transferred),
+// allowing `*this` to be used as a ReadHandler
+//
+template<
+ class AsyncStream,
+ class DynamicBuffer,
+ class Handler>
+void
+detect_ssl_op::
+operator()(error_code ec, std::size_t bytes_transferred, bool cont)
+{
+ namespace beast = boost::beast;
+
+ // This introduces the scope of the stackless coroutine
+ reenter(*this)
+ {
+ // Loop until an error occurs or we get a definitive answer
+ for(;;)
+ {
+ // There could already be a hello in the buffer so check first
+ result_ = is_tls_client_hello(buffer_.data());
+
+ // If we got an answer, then the operation is complete
+ if(! boost::indeterminate(result_))
+ break;
+
+ // Try to fill our buffer by reading from the stream.
+ // The function read_size calculates a reasonable size for the
+ // amount to read next, using existing capacity if possible to
+ // avoid allocating memory, up to the limit of 1536 bytes which
+ // is the size of a normal TCP frame.
+ //
+ // `async_read_some` expects a ReadHandler as the completion
+ // handler. The signature of a read handler is void(error_code, size_t),
+ // and this function matches that signature (the `cont` parameter has
+ // a default of true). We pass `std::move(*this)` as the completion
+ // handler for the read operation. This transfers ownership of this
+ // entire state machine back into the `async_read_some` operation.
+ // Care must be taken with this idiom, to ensure that parameters
+ // passed to the initiating function which could be invalidated
+ // by the move, are first moved to the stack before calling the
+ // initiating function.
+
+ yield stream_.async_read_some(buffer_.prepare(
+ read_size(buffer_, 1536)), std::move(*this));
+
+ // Commit what we read into the buffer's input area.
+ buffer_.commit(bytes_transferred);
+
+ // Check for an error
+ if(ec)
+ break;
+ }
+
+ // Invoke the final handler.
+ // If `cont` is true, the handler will be invoked directly.
+ // Otherwise, the handler will be submitted to the executor
+ // through a call to `net::post`.
+
+ this->invoke(cont, ec, result_);
+ }
+}
+
+// Including this file undefines the macros used by the stackless fauxroutines.
+#include
+
+} // detail
+
+//]
+
+} // beast
+} // boost
+
+#endif
diff --git a/test/beast/core/CMakeLists.txt b/test/beast/core/CMakeLists.txt
index 6fe50495..3606549d 100644
--- a/test/beast/core/CMakeLists.txt
+++ b/test/beast/core/CMakeLists.txt
@@ -44,6 +44,7 @@ add_executable (tests-beast-core
buffers_range.cpp
buffers_suffix.cpp
buffers_to_string.cpp
+ detect_ssl.cpp
dynamic_buffer_ref.cpp
error.cpp
file.cpp
diff --git a/test/beast/core/Jamfile b/test/beast/core/Jamfile
index 3bab6352..0bd70b00 100644
--- a/test/beast/core/Jamfile
+++ b/test/beast/core/Jamfile
@@ -32,6 +32,7 @@ local SOURCES =
buffers_range.cpp
buffers_suffix.cpp
buffers_to_string.cpp
+ detect_ssl.cpp
dynamic_buffer_ref.cpp
error.cpp
file.cpp
diff --git a/test/beast/core/detect_ssl.cpp b/test/beast/core/detect_ssl.cpp
new file mode 100644
index 00000000..7e457414
--- /dev/null
+++ b/test/beast/core/detect_ssl.cpp
@@ -0,0 +1,181 @@
+//
+// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+// Test that header file is self-contained.
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+namespace boost {
+namespace beast {
+
+class detect_ssl_test : public unit_test::suite
+{
+public:
+ void
+ testDetect()
+ {
+ auto const yes =
+ [](int n, string_view s)
+ {
+ BEAST_EXPECT(detail::is_tls_client_hello(
+ net::const_buffer(s.data(), n)));
+ };
+
+ auto const no =
+ [](int n, string_view s)
+ {
+ BEAST_EXPECT(! detail::is_tls_client_hello(
+ net::const_buffer(s.data(), n)));
+ };
+
+ auto const maybe =
+ [](int n, string_view s)
+ {
+ BEAST_EXPECT(boost::indeterminate(
+ detail::is_tls_client_hello(
+ net::const_buffer(s.data(), n))));
+ };
+
+ maybe( 0, "\x00\x00\x00\x00\x00\x00\x00\x00\x00");
+ no ( 1, "\x01\x00\x00\x00\x00\x00\x00\x00\x00");
+ maybe( 1, "\x16\x00\x00\x00\x00\x00\x00\x00\x00");
+ maybe( 4, "\x16\x00\x00\x00\x00\x00\x00\x00\x00");
+ no ( 5, "\x16\x00\x00\x00\x00\x00\x00\x00\x00");
+ maybe( 5, "\x16\x00\x00\x01\x00\x00\x00\x00\x00");
+ no ( 8, "\x16\x00\x00\x01\x00\x00\x00\x00\x00");
+ maybe( 8, "\x16\x00\x00\x01\x00\x01\x00\x00\x00");
+ no ( 9, "\x16\x00\x00\x01\x00\x01\x01\x00\x00");
+ yes ( 9, "\x16\x00\x00\x01\x00\x01\x00\x00\x00");
+ }
+
+ void
+ testRead()
+ {
+ net::io_context ioc;
+
+ // true
+
+ {
+ error_code ec;
+ flat_buffer b;
+ test::stream s1(ioc);
+ s1.append({"\x16\x00\x00\x01\x00\x01\x00\x00\x00", 9});
+ auto result = detect_ssl(s1, b, ec);
+ BEAST_EXPECT(result == true);
+ BEAST_EXPECTS(! ec, ec.message());
+ }
+
+ // true
+
+ {
+ error_code ec;
+ flat_buffer b;
+ test::stream s1(ioc);
+ auto s2 = test::connect(s1);
+ s1.append({"\x16\x00\x00\x01\x00\x01\x00\x00\x00", 9});
+ s2.close();
+ auto result = detect_ssl(s1, b, ec);
+ BEAST_EXPECT(result == true);
+ BEAST_EXPECTS(! ec, ec.message());
+ }
+
+ // false
+
+ {
+ error_code ec;
+ flat_buffer b;
+ test::stream s1(ioc);
+ s1.append({"\x16\x00\x00\x01\x00\x01\x01\x00\x00", 9});
+ auto result = detect_ssl(s1, b, ec);
+ BEAST_EXPECT(result == false);
+ BEAST_EXPECTS(! ec, ec.message());
+ }
+
+ // eof
+ {
+ error_code ec;
+ flat_buffer b;
+ test::stream s1(ioc);
+ auto s2 = test::connect(s1);
+ s1.append({"\x16\x00\x00\x01\x00", 5});
+ s2.close();
+ auto result = detect_ssl(s1, b, ec);
+ BEAST_EXPECT(result == false);
+ BEAST_EXPECT(ec);
+ }
+ }
+
+ void
+ testAsyncRead()
+ {
+ net::io_context ioc;
+
+ // true
+
+ {
+ flat_buffer b;
+ test::stream s1(ioc);
+ s1.append({"\x16\x00\x00\x01\x00\x01\x00\x00\x00", 9});
+ async_detect_ssl(s1, b, test::success_handler());
+ test::run(ioc);
+ }
+
+ // true
+
+ {
+ flat_buffer b;
+ test::stream s1(ioc);
+ auto s2 = test::connect(s1);
+ s1.append({"\x16\x00\x00\x01\x00\x01\x00\x00\x00", 9});
+ s2.close();
+ async_detect_ssl(s1, b, test::success_handler());
+ test::run(ioc);
+ }
+
+ // false
+
+ {
+ flat_buffer b;
+ test::stream s1(ioc);
+ s1.append({"\x16\x00\x00\x01\x00\x01\x01\x00\x00", 9});
+ async_detect_ssl(s1, b, test::success_handler());
+ test::run(ioc);
+ }
+
+ // eof
+ {
+ flat_buffer b;
+ test::stream s1(ioc);
+ auto s2 = test::connect(s1);
+ s1.append({"\x16\x00\x00\x01\x00", 5});
+ s2.close();
+ async_detect_ssl(s1, b,
+ test::fail_handler(net::error::eof));
+ test::run(ioc);
+ }
+ }
+
+ void
+ run() override
+ {
+ testDetect();
+ testRead();
+ testAsyncRead();
+ }
+};
+
+BEAST_DEFINE_TESTSUITE(beast,core,detect_ssl);
+
+} // beast
+} // boost
diff --git a/test/doc/CMakeLists.txt b/test/doc/CMakeLists.txt
index 106a480b..ec6fcb40 100644
--- a/test/doc/CMakeLists.txt
+++ b/test/doc/CMakeLists.txt
@@ -19,7 +19,6 @@ add_executable (tests-doc
Jamfile
snippets.hpp
snippets.ipp
- core_examples.cpp
core_snippets.cpp
core_1_refresher.cpp
core_3_layers.cpp
diff --git a/test/doc/Jamfile b/test/doc/Jamfile
index 4a3abefc..44161a1c 100644
--- a/test/doc/Jamfile
+++ b/test/doc/Jamfile
@@ -19,7 +19,6 @@ alias run-tests :
[ compile core_snippets.cpp ]
[ compile http_snippets.cpp ]
[ compile websocket_snippets.cpp ]
- [ run core_examples.cpp $(TEST_MAIN) ]
[ run core_1_refresher.cpp $(TEST_MAIN) ]
[ run core_3_layers.cpp $(TEST_MAIN) ]
[ run http_examples.cpp $(TEST_MAIN) ]
@@ -28,7 +27,6 @@ alias run-tests :
exe fat-tests :
$(TEST_MAIN)
- core_examples.cpp
core_1_refresher.cpp
core_3_layers.cpp
http_examples.cpp
diff --git a/test/doc/core_examples.cpp b/test/doc/core_examples.cpp
deleted file mode 100644
index 30afc9e9..00000000
--- a/test/doc/core_examples.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-//
-// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/boostorg/beast
-//
-
-#include "example/common/detect_ssl.hpp"
-
-#include
-#include
-#include
-#include
-#include
-
-namespace boost {
-namespace beast {
-
-class examples_test
- : public beast::unit_test::suite
- , public beast::test::enable_yield_to
-{
-public:
- void
- testDetect()
- {
- char buf[4];
- buf[0] = 0x16;
- buf[1] = 0;
- buf[2] = 0;
- buf[3] = 0;
- BEAST_EXPECT(boost::indeterminate(is_ssl_handshake(
- net::buffer(buf, 0))));
- BEAST_EXPECT(boost::indeterminate(is_ssl_handshake(
- net::buffer(buf, 1))));
- BEAST_EXPECT(boost::indeterminate(is_ssl_handshake(
- net::buffer(buf, 2))));
- BEAST_EXPECT(boost::indeterminate(is_ssl_handshake(
- net::buffer(buf, 3))));
- BEAST_EXPECT(is_ssl_handshake(
- net::buffer(buf, 4)));
- buf[0] = 0;
- BEAST_EXPECT(! is_ssl_handshake(
- net::buffer(buf, 1)));
- }
-
- void
- testRead()
- {
- {
- test::stream ts{ioc_, "\x16***"};
- error_code ec;
- flat_buffer b;
- auto const result = detect_ssl(ts, b, ec);
- BEAST_EXPECTS(! ec, ec.message());
- BEAST_EXPECT(result);
- }
- yield_to(
- [&](yield_context yield)
- {
- test::stream ts{ioc_, "\x16***"};
- error_code ec;
- flat_buffer b;
- auto const result =
- async_detect_ssl(ts, b, yield[ec]);
- BEAST_EXPECTS(! ec, ec.message());
- BEAST_EXPECT(result);
- });
- }
-
- void
- run()
- {
- testDetect();
- testRead();
- }
-};
-
-BEAST_DEFINE_TESTSUITE(beast,core,examples);
-
-} // beast
-} // boost
diff --git a/test/example/common/CMakeLists.txt b/test/example/common/CMakeLists.txt
index 5ee13696..5d1f8f59 100644
--- a/test/example/common/CMakeLists.txt
+++ b/test/example/common/CMakeLists.txt
@@ -18,7 +18,6 @@ add_executable (tests-example-common
${EXTRAS_FILES}
${TEST_MAIN}
Jamfile
- detect_ssl.cpp
root_certificates.cpp
server_certificate.cpp
session_alloc.cpp
diff --git a/test/example/common/Jamfile b/test/example/common/Jamfile
index 76ba448e..7220bc52 100644
--- a/test/example/common/Jamfile
+++ b/test/example/common/Jamfile
@@ -8,7 +8,6 @@
#
local SOURCES =
- detect_ssl.cpp
root_certificates.cpp
server_certificate.cpp
session_alloc.cpp
diff --git a/test/example/common/detect_ssl.cpp b/test/example/common/detect_ssl.cpp
deleted file mode 100644
index 3596fccc..00000000
--- a/test/example/common/detect_ssl.cpp
+++ /dev/null
@@ -1,12 +0,0 @@
-//
-// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/boostorg/beast
-//
-
-// Test that header file is self-contained.
-#include "example/common/detect_ssl.hpp"
-