[/ 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 ] [section:detect_ssl Detect SSL __example__] 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 requirements, 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] The examples [path_link example/advanced/server/advanced_server.cpp advanced-server] and [path_link example/advanced/server-flex/advanced_server_flex.cpp advanced-server-flex] use this SSL detection function. [endsect]