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