9
3

7b_detect_ssl.qbk 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. [/
  2. Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
  3. Distributed under the Boost Software License, Version 1.0. (See accompanying
  4. file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. Official repository: https://github.com/boostorg/beast
  6. ]
  7. [section:detect_ssl Detect SSL __example__]
  8. In this example we will build a simple function to detect the presence of the
  9. [@https://tools.ietf.org/html/rfc2246#section-7.4 TLS client handshake]
  10. given an input buffer sequence. Then we build on the example by adding a
  11. synchronous stream algorithm. Finally, we implement an asynchronous
  12. detection function using a composed operation. This SSL detector may
  13. be used to allow a server to accept both TLS and plain (unencrypted)
  14. connections at the same port.
  15. Here is the declaration for a function template to detect the SSL client
  16. handshake. The function accepts any object whose type meets the requirements
  17. of __ConstBufferSequence__. This gives callers flexibility to use a buffer
  18. object whose behavior is appropriate to the task.
  19. [example_core_detect_ssl_1]
  20. The algorithm examines the buffer starting from the beginning, and
  21. performs a series of qualifying checks against the TLS specification.
  22. When not enough data exists to be certain, the returned value of
  23. `boost::indeterminate` informs the caller to read more data into the buffer.
  24. The function definition for the declaration above follows:
  25. [example_core_detect_ssl_2]
  26. The detection function above is suitably generic and targeted in
  27. focus that it may be used as a building block to create higher level
  28. abstractions. Our goal is to create a ['stream algorithm]: a function
  29. which is invoked with a stream, that reads or writes (or both) to achieve
  30. a purpose. In this case, to detect the TLS client handshake. Stream
  31. algorithms may be synchronous or asynchronous. Because synchronous algorithms
  32. are easier to write, we start there. Then we build the asynchronous version,
  33. trying to model it similarly to make reasoning about it easier.
  34. The synchronous version is implemented thusly:
  35. [example_core_detect_ssl_3]
  36. Now that we have the synchronous version, we can attempt to model the
  37. asynchronous version similarly. A function which launches an asynchronous
  38. operation is called an ['initiating function]. While the synchronous
  39. version above produces an error code through an output parameter, the
  40. asynchronous version delivers the error code to a completion handler
  41. or other custom mechanism defined by the completion token. The signature
  42. of the initiating function reflects these differences.
  43. First we declare the initiating function and document the requirements,
  44. parameters, preconditions, and effects:
  45. [example_core_detect_ssl_4]
  46. There are two additional components required to implement the initiating
  47. function:
  48. * An intermediate completion handler, called the "composed operation"
  49. object, which holds the state of the operation while it is in progress,
  50. and also holds the user's completion handler to be invoked when the
  51. opeartion completes, and
  52. * An "initiation" function object which when invoked with parameters
  53. captured at the call site of the initiating function, constructs the
  54. composed operation with the captured arguments and launches it.
  55. Here we forward declare the composed operation type, and provide the
  56. definition of the initiation function object. They are placed in the
  57. `detail` namespace since they should not be public:
  58. [example_core_detect_ssl_5]
  59. The initiating function definition itself is straightforward. We perform
  60. type checking on the parameters, and then let `net::async_initiate`
  61. capture the parameter list along with a copy of our initiation function
  62. object. Depending on the specialization of `async_result` for the type
  63. of `CompletionToken`, the initiation function may be invoked immediately.
  64. Alternatively, it may be invoked later, after the initiating function
  65. returns. This is known as "lazy execution," and allows efficient and
  66. expressive abstractions to be written.
  67. [example_core_detect_ssl_6]
  68. Now we will declare our composed operation. There is a considerable
  69. amount of necessary boilerplate to get this right, but the result
  70. is worth the effort.
  71. [example_core_detect_ssl_7]
  72. The boilerplate is all done, and now we need to implement the function
  73. call operator that turns this composed operation a completion handler
  74. with the signature `void(error_code, std::size_t)` which is exactly
  75. the signature needed when performing asynchronous reads. This function
  76. is a transformation of the synchronous version of `detect_ssl` above,
  77. but with the inversion of flow that characterizes code written in the
  78. callback style:
  79. [example_core_detect_ssl_8]
  80. The examples
  81. [path_link example/advanced/server/advanced_server.cpp advanced-server] and
  82. [path_link example/advanced/server-flex/advanced_server_flex.cpp advanced-server-flex]
  83. use this SSL detection function.
  84. [endsect]