beast/doc/qbk/03_core/7b_detect_ssl.qbk
2019-07-07 12:29:56 -07:00

112 lines
4.8 KiB
Plaintext

[/
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]