mirror of
https://github.com/boostorg/beast.git
synced 2025-07-30 21:07:26 +02:00
detect_ssl, async_detect_ssl are public interfaces
This commit is contained in:
@ -1,3 +1,9 @@
|
|||||||
|
Version 218:
|
||||||
|
|
||||||
|
* detect_ssl, async_detect_ssl are public interfaces
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
Version 217:
|
Version 217:
|
||||||
|
|
||||||
* websocket idle pings
|
* websocket idle pings
|
||||||
|
@ -95,6 +95,5 @@ effect:
|
|||||||
[include 4_buffers.qbk]
|
[include 4_buffers.qbk]
|
||||||
[include 5_files.qbk]
|
[include 5_files.qbk]
|
||||||
[include 6_composed.qbk]
|
[include 6_composed.qbk]
|
||||||
[include 7_detect_ssl.qbk]
|
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
]
|
]
|
||||||
|
|
||||||
[section Writing Composed Operations]
|
[section Writing Composed Operations]
|
||||||
[block'''<?dbhtml stop-chunking?>''']
|
|
||||||
|
|
||||||
Asynchronous operations are started by calling a free function or member
|
Asynchronous operations are started by calling a free function or member
|
||||||
function known as an asynchronous ['__async_initfn__]. This function accepts
|
function known as an asynchronous ['__async_initfn__]. This function accepts
|
||||||
@ -111,80 +110,7 @@ composed operations:
|
|||||||
]]
|
]]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[include 6a_echo.qbk]
|
||||||
|
[include 6b_detect_ssl.qbk]
|
||||||
[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]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
80
doc/qbk/03_core/6a_echo.qbk
Normal file
80
doc/qbk/03_core/6a_echo.qbk
Normal file
@ -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]
|
109
doc/qbk/03_core/6b_detect_ssl.qbk
Normal file
109
doc/qbk/03_core/6b_detect_ssl.qbk
Normal file
@ -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]
|
@ -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]
|
|
@ -117,7 +117,6 @@
|
|||||||
the paths close to absolute.
|
the paths close to absolute.
|
||||||
]
|
]
|
||||||
|
|
||||||
[import ../../example/common/detect_ssl.hpp]
|
|
||||||
[import ../../example/doc/http_examples.hpp]
|
[import ../../example/doc/http_examples.hpp]
|
||||||
[import ../../example/echo-op/echo_op.cpp]
|
[import ../../example/echo-op/echo_op.cpp]
|
||||||
[import ../../example/http/client/sync/http_client_sync.cpp]
|
[import ../../example/http/client/sync/http_client_sync.cpp]
|
||||||
@ -134,6 +133,7 @@
|
|||||||
[import ../../test/doc/core_3_layers.cpp]
|
[import ../../test/doc/core_3_layers.cpp]
|
||||||
[import ../../test/doc/websocket_3_handshake.cpp]
|
[import ../../test/doc/websocket_3_handshake.cpp]
|
||||||
|
|
||||||
|
[import ../../include/boost/beast/core/detect_ssl.hpp]
|
||||||
[import ../../test/beast/core/rate_policy.cpp]
|
[import ../../test/beast/core/rate_policy.cpp]
|
||||||
|
|
||||||
[section:quickref Reference]
|
[section:quickref Reference]
|
||||||
|
@ -64,10 +64,12 @@
|
|||||||
<bridgehead renderas="sect3">Functions</bridgehead>
|
<bridgehead renderas="sect3">Functions</bridgehead>
|
||||||
<simplelist type="vert" columns="1">
|
<simplelist type="vert" columns="1">
|
||||||
<member><link linkend="beast.ref.boost__beast__allocate_stable">allocate_stable</link> <emphasis role="green">🞲</emphasis></member>
|
<member><link linkend="beast.ref.boost__beast__allocate_stable">allocate_stable</link> <emphasis role="green">🞲</emphasis></member>
|
||||||
|
<member><link linkend="beast.ref.boost__beast__async_detect_ssl">async_detect_ssl</link> <emphasis role="green">🞲</emphasis></member>
|
||||||
<member><link linkend="beast.ref.boost__beast__beast_close_socket">beast_close_socket</link> <emphasis role="green">🞲</emphasis></member>
|
<member><link linkend="beast.ref.boost__beast__beast_close_socket">beast_close_socket</link> <emphasis role="green">🞲</emphasis></member>
|
||||||
<member><link linkend="beast.ref.boost__beast__bind_front_handler">bind_front_handler</link> <emphasis role="green">🞲</emphasis></member>
|
<member><link linkend="beast.ref.boost__beast__bind_front_handler">bind_front_handler</link> <emphasis role="green">🞲</emphasis></member>
|
||||||
<member><link linkend="beast.ref.boost__beast__bind_handler">bind_handler</link></member>
|
<member><link linkend="beast.ref.boost__beast__bind_handler">bind_handler</link></member>
|
||||||
<member><link linkend="beast.ref.boost__beast__close_socket">close_socket</link> <emphasis role="green">🞲</emphasis></member>
|
<member><link linkend="beast.ref.boost__beast__close_socket">close_socket</link> <emphasis role="green">🞲</emphasis></member>
|
||||||
|
<member><link linkend="beast.ref.boost__beast__detect_ssl">detect_ssl</link> <emphasis role="green">🞲</emphasis></member>
|
||||||
<member><link linkend="beast.ref.boost__beast__generic_category">generic_category</link></member>
|
<member><link linkend="beast.ref.boost__beast__generic_category">generic_category</link></member>
|
||||||
<member><link linkend="beast.ref.boost__beast__get_lowest_layer">get_lowest_layer</link> <emphasis role="green">🞲</emphasis></member>
|
<member><link linkend="beast.ref.boost__beast__get_lowest_layer">get_lowest_layer</link> <emphasis role="green">🞲</emphasis></member>
|
||||||
<member><link linkend="beast.ref.boost__beast__iequals">iequals</link></member>
|
<member><link linkend="beast.ref.boost__beast__iequals">iequals</link></member>
|
||||||
|
@ -35,6 +35,13 @@
|
|||||||
!
|
!
|
||||||
* Chat with us at the [*#beast] and [*#boost] channels in the
|
* Chat with us at the [*#beast] and [*#boost] channels in the
|
||||||
[@https://cppalliance.org/slack/ [*C++ Slack Workspace]].
|
[@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 `basic_stream`] and
|
||||||
[link beast.ref.boost__beast__basic_stream `tcp_stream`] offer:
|
[link beast.ref.boost__beast__basic_stream `tcp_stream`] offer:
|
||||||
* Timeouts:
|
* Timeouts:
|
||||||
@ -47,8 +54,9 @@
|
|||||||
[link beast.ref.boost__beast__unlimited_rate_policy `unlimited`],
|
[link beast.ref.boost__beast__unlimited_rate_policy `unlimited`],
|
||||||
or a user-defined
|
or a user-defined
|
||||||
[link beast.concepts.RatePolicy ['RatePolicy]]!
|
[link beast.concepts.RatePolicy ['RatePolicy]]!
|
||||||
* [[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html P1322R0]]
|
* Put the strand directly on the socket using
|
||||||
Put the strand on the socket itself, no more `bind_executor` at call sites!
|
[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html P1322R0],
|
||||||
|
no more `bind_executor` at call sites!
|
||||||
* Base classes
|
* Base classes
|
||||||
[link beast.ref.boost__beast__async_op_base `async_op_base`] and
|
[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`]
|
[link beast.ref.boost__beast__stable_async_op_base `stable_async_op_base`]
|
||||||
@ -56,10 +64,7 @@
|
|||||||
* All asynchronous operations use Asio's
|
* All asynchronous operations use Asio's
|
||||||
[@boost:/doc/html/boost_asio/reference/async_initiate.html `async_initiate`]
|
[@boost:/doc/html/boost_asio/reference/async_initiate.html `async_initiate`]
|
||||||
for efficient integration with Coroutines TS.
|
for efficient integration with Coroutines TS.
|
||||||
* New [@../../example/websocket/server/chat-multi websocket-chat-multi]
|
* OpenSSL is now required to build tests and examples
|
||||||
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
|
|
||||||
* See the full [link beast.release_notes [*Release Notes]] for a complete list
|
* See the full [link beast.release_notes [*Release Notes]] for a complete list
|
||||||
of changes.
|
of changes.
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@ if (OPENSSL_FOUND)
|
|||||||
|
|
||||||
add_executable (advanced-server-flex
|
add_executable (advanced-server-flex
|
||||||
${BOOST_BEAST_FILES}
|
${BOOST_BEAST_FILES}
|
||||||
${PROJECT_SOURCE_DIR}/example/common/detect_ssl.hpp
|
|
||||||
${PROJECT_SOURCE_DIR}/example/common/server_certificate.hpp
|
${PROJECT_SOURCE_DIR}/example/common/server_certificate.hpp
|
||||||
Jamfile
|
Jamfile
|
||||||
advanced_server_flex.cpp
|
advanced_server_flex.cpp
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
//
|
//
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
#include "example/common/detect_ssl.hpp"
|
|
||||||
#include "example/common/server_certificate.hpp"
|
#include "example/common/server_certificate.hpp"
|
||||||
|
|
||||||
#include <boost/beast/core.hpp>
|
#include <boost/beast/core.hpp>
|
||||||
@ -822,7 +821,7 @@ public:
|
|||||||
// Set the timeout.
|
// Set the timeout.
|
||||||
stream_.expires_after(std::chrono::seconds(30));
|
stream_.expires_after(std::chrono::seconds(30));
|
||||||
|
|
||||||
async_detect_ssl(
|
beast::async_detect_ssl(
|
||||||
stream_,
|
stream_,
|
||||||
buffer_,
|
buffer_,
|
||||||
beast::bind_front_handler(
|
beast::bind_front_handler(
|
||||||
|
@ -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 <boost/assert.hpp>
|
|
||||||
#include <boost/config.hpp>
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
// Example: Detect TLS/SSL
|
|
||||||
//
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
//[example_core_detect_ssl_1
|
|
||||||
|
|
||||||
#include <boost/beast/core.hpp>
|
|
||||||
#include <boost/asio/coroutine.hpp>
|
|
||||||
#include <boost/asio/executor_work_guard.hpp>
|
|
||||||
#include <boost/logic/tribool.hpp>
|
|
||||||
|
|
||||||
/** 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<class ConstBufferSequence>
|
|
||||||
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<ConstBufferSequence>::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<SyncReadStream>::value,
|
|
||||||
"SyncReadStream type requirements not met");
|
|
||||||
static_assert(
|
|
||||||
boost::asio::is_dynamic_buffer<DynamicBuffer>::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
|
|
||||||
<em>composed operation</em>. 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<AsyncReadStream>::value,
|
|
||||||
"SyncReadStream type requirements not met");
|
|
||||||
static_assert(
|
|
||||||
boost::asio::is_dynamic_buffer<DynamicBuffer>::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<error_code, boost::tribool> 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<T>, 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<AsyncReadStream&>().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<class DeducedHandler>
|
|
||||||
detect_ssl_op(
|
|
||||||
AsyncReadStream& stream,
|
|
||||||
DynamicBuffer& buffer,
|
|
||||||
DeducedHandler&& handler)
|
|
||||||
: stream_(stream)
|
|
||||||
, work_(stream.get_executor())
|
|
||||||
, buffer_(buffer)
|
|
||||||
, handler_(std::forward<DeducedHandler>(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<Handler>;
|
|
||||||
|
|
||||||
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<AsyncReadStream&>().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<AsyncStream, DynamicBuffer, Handler>::
|
|
||||||
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
|
|
@ -101,6 +101,11 @@ async_echo (
|
|||||||
template<class AsyncStream, class Handler>
|
template<class AsyncStream, class Handler>
|
||||||
class echo_op;
|
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 <boost/asio/yield.hpp>
|
||||||
|
|
||||||
// Read a line and echo it back
|
// Read a line and echo it back
|
||||||
//
|
//
|
||||||
template<
|
template<
|
||||||
@ -153,12 +158,6 @@ async_echo(
|
|||||||
beast::executor_type<AsyncStream> /*< The type of executor used by the stream to dispatch asynchronous operations >*/
|
beast::executor_type<AsyncStream> /*< 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 <boost/asio/yield.hpp>
|
|
||||||
|
|
||||||
// This nested class implements the echo composed operation as a
|
// This nested class implements the echo composed operation as a
|
||||||
// stateful completion handler. We derive from `async_op_base` to
|
// stateful completion handler. We derive from `async_op_base` to
|
||||||
// take care of boilerplate and we derived from net::coroutine 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 <boost/asio/yield.hpp>
|
|
||||||
|
|
||||||
// Create the composed operation and launch it. This is a constructor
|
// Create the composed operation and launch it. This is a constructor
|
||||||
// call followed by invocation of operator(). We use BOOST_ASIO_HANDLER_TYPE
|
// call followed by invocation of operator(). We use BOOST_ASIO_HANDLER_TYPE
|
||||||
// to convert the completion token into the correct handler type,
|
// to convert the completion token into the correct handler type,
|
||||||
@ -332,6 +328,9 @@ async_echo(
|
|||||||
return init.result.get();
|
return init.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Including this file undefines the macros used by the stackless fauxroutines.
|
||||||
|
#include <boost/asio/unyield.hpp>
|
||||||
|
|
||||||
//]
|
//]
|
||||||
|
|
||||||
struct move_only_handler
|
struct move_only_handler
|
||||||
|
@ -14,7 +14,6 @@ if (OPENSSL_FOUND)
|
|||||||
|
|
||||||
add_executable (http-server-flex
|
add_executable (http-server-flex
|
||||||
${BOOST_BEAST_FILES}
|
${BOOST_BEAST_FILES}
|
||||||
${PROJECT_SOURCE_DIR}/example/common/detect_ssl.hpp
|
|
||||||
${PROJECT_SOURCE_DIR}/example/common/server_certificate.hpp
|
${PROJECT_SOURCE_DIR}/example/common/server_certificate.hpp
|
||||||
Jamfile
|
Jamfile
|
||||||
http_server_flex.cpp
|
http_server_flex.cpp
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
//
|
//
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
#include "example/common/detect_ssl.hpp"
|
|
||||||
#include "example/common/server_certificate.hpp"
|
#include "example/common/server_certificate.hpp"
|
||||||
|
|
||||||
#include <boost/beast/core.hpp>
|
#include <boost/beast/core.hpp>
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||||
#include <boost/beast/core/error.hpp>
|
#include <boost/beast/core/error.hpp>
|
||||||
|
#include <boost/asio/io_context.hpp>
|
||||||
#include <boost/core/exchange.hpp>
|
#include <boost/core/exchange.hpp>
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
@ -140,6 +141,46 @@ fail_handler(error_code ec) noexcept
|
|||||||
return handler(ec);
|
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<class Rep, class Period>
|
||||||
|
void
|
||||||
|
run_for(
|
||||||
|
net::io_context& ioc,
|
||||||
|
std::chrono::duration<Rep, Period> elapsed)
|
||||||
|
{
|
||||||
|
ioc.run_for(elapsed);
|
||||||
|
ioc.restart();
|
||||||
|
}
|
||||||
|
|
||||||
} // test
|
} // test
|
||||||
} // beast
|
} // beast
|
||||||
} // boost
|
} // boost
|
||||||
|
@ -21,46 +21,6 @@ namespace boost {
|
|||||||
namespace beast {
|
namespace beast {
|
||||||
namespace test {
|
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<class Rep, class Period>
|
|
||||||
void
|
|
||||||
run_for(
|
|
||||||
net::io_context& ioc,
|
|
||||||
std::chrono::duration<Rep, Period> elapsed)
|
|
||||||
{
|
|
||||||
ioc.run_for(elapsed);
|
|
||||||
ioc.restart();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Connect two TCP sockets together.
|
/** Connect two TCP sockets together.
|
||||||
*/
|
*/
|
||||||
template<class Executor>
|
template<class Executor>
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include <boost/beast/core/buffers_range.hpp>
|
#include <boost/beast/core/buffers_range.hpp>
|
||||||
#include <boost/beast/core/buffers_suffix.hpp>
|
#include <boost/beast/core/buffers_suffix.hpp>
|
||||||
#include <boost/beast/core/buffers_to_string.hpp>
|
#include <boost/beast/core/buffers_to_string.hpp>
|
||||||
|
#include <boost/beast/core/detect_ssl.hpp>
|
||||||
#include <boost/beast/core/dynamic_buffer_ref.hpp>
|
#include <boost/beast/core/dynamic_buffer_ref.hpp>
|
||||||
#include <boost/beast/core/error.hpp>
|
#include <boost/beast/core/error.hpp>
|
||||||
#include <boost/beast/core/file.hpp>
|
#include <boost/beast/core/file.hpp>
|
||||||
|
612
include/boost/beast/core/detect_ssl.hpp
Normal file
612
include/boost/beast/core/detect_ssl.hpp
Normal file
@ -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 <boost/beast/core/detail/config.hpp>
|
||||||
|
#include <boost/beast/core/async_op_base.hpp>
|
||||||
|
#include <boost/beast/core/error.hpp>
|
||||||
|
#include <boost/beast/core/read_size.hpp>
|
||||||
|
#include <boost/beast/core/stream_traits.hpp>
|
||||||
|
#include <boost/logic/tribool.hpp>
|
||||||
|
#include <boost/asio/async_result.hpp>
|
||||||
|
#include <boost/asio/coroutine.hpp>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
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 <em>ConstBufferSequence</em>.
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
|
<a href="https://tools.ietf.org/html/rfc2246#section-7.4">7.4. Handshake protocol</a>
|
||||||
|
(RFC2246: The TLS Protocol)
|
||||||
|
*/
|
||||||
|
template <class ConstBufferSequence>
|
||||||
|
boost::tribool
|
||||||
|
is_tls_client_hello (ConstBufferSequence const& buffers);
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
//]
|
||||||
|
|
||||||
|
//[example_core_detect_ssl_2
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <class ConstBufferSequence>
|
||||||
|
boost::tribool
|
||||||
|
is_tls_client_hello (ConstBufferSequence const& buffers)
|
||||||
|
{
|
||||||
|
// Make sure buffers meets the requirements
|
||||||
|
static_assert(
|
||||||
|
net::is_const_buffer_sequence<ConstBufferSequence>::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 <em>composed operation</em>, 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 <em>SyncReadStream</em>.
|
||||||
|
|
||||||
|
@param buffer The dynamic buffer to use. This type must meet the
|
||||||
|
requirements of <em>DynamicBuffer</em>.
|
||||||
|
|
||||||
|
@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<SyncReadStream>::value,
|
||||||
|
"SyncReadStream type requirements not met");
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
net::is_dynamic_buffer<DynamicBuffer>::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 <em>composed asynchronous operation</em>,
|
||||||
|
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 <em>AsyncReadStream</em>.
|
||||||
|
|
||||||
|
@param buffer The dynamic buffer to use. This type must meet the
|
||||||
|
requirements of <em>DynamicBuffer</em>.
|
||||||
|
|
||||||
|
@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<CompletionToken>::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<DetectHandler>::type,
|
||||||
|
AsyncReadStream,
|
||||||
|
DynamicBuffer>(
|
||||||
|
std::forward<DetectHandler>(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<CompletionToken>::type,
|
||||||
|
void(error_code, boost::tribool)>::return_type
|
||||||
|
{
|
||||||
|
// Make sure arguments meet the type requirements
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
is_async_read_stream<AsyncReadStream>::value,
|
||||||
|
"SyncReadStream type requirements not met");
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
net::is_dynamic_buffer<DynamicBuffer>::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<AsyncReadStream>>
|
||||||
|
{
|
||||||
|
// 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<class DetectHandler_>
|
||||||
|
detect_ssl_op(
|
||||||
|
DetectHandler_&& handler,
|
||||||
|
AsyncReadStream& stream,
|
||||||
|
DynamicBuffer& buffer)
|
||||||
|
: async_op_base<DetectHandler_,
|
||||||
|
executor_type<AsyncReadStream>>(
|
||||||
|
std::forward<DetectHandler_>(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 <boost/asio/yield.hpp>
|
||||||
|
|
||||||
|
// 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<AsyncStream, DynamicBuffer, Handler>::
|
||||||
|
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 <boost/asio/unyield.hpp>
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
//]
|
||||||
|
|
||||||
|
} // beast
|
||||||
|
} // boost
|
||||||
|
|
||||||
|
#endif
|
@ -44,6 +44,7 @@ add_executable (tests-beast-core
|
|||||||
buffers_range.cpp
|
buffers_range.cpp
|
||||||
buffers_suffix.cpp
|
buffers_suffix.cpp
|
||||||
buffers_to_string.cpp
|
buffers_to_string.cpp
|
||||||
|
detect_ssl.cpp
|
||||||
dynamic_buffer_ref.cpp
|
dynamic_buffer_ref.cpp
|
||||||
error.cpp
|
error.cpp
|
||||||
file.cpp
|
file.cpp
|
||||||
|
@ -32,6 +32,7 @@ local SOURCES =
|
|||||||
buffers_range.cpp
|
buffers_range.cpp
|
||||||
buffers_suffix.cpp
|
buffers_suffix.cpp
|
||||||
buffers_to_string.cpp
|
buffers_to_string.cpp
|
||||||
|
detect_ssl.cpp
|
||||||
dynamic_buffer_ref.cpp
|
dynamic_buffer_ref.cpp
|
||||||
error.cpp
|
error.cpp
|
||||||
file.cpp
|
file.cpp
|
||||||
|
181
test/beast/core/detect_ssl.cpp
Normal file
181
test/beast/core/detect_ssl.cpp
Normal file
@ -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 <boost/beast/core/detect_ssl.hpp>
|
||||||
|
|
||||||
|
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||||
|
#include <boost/beast/_experimental/test/stream.hpp>
|
||||||
|
#include <boost/beast/_experimental/test/handler.hpp>
|
||||||
|
#include <boost/beast/core/flat_buffer.hpp>
|
||||||
|
#include <boost/beast/core/string.hpp>
|
||||||
|
#include <boost/core/exchange.hpp>
|
||||||
|
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
|
@ -19,7 +19,6 @@ add_executable (tests-doc
|
|||||||
Jamfile
|
Jamfile
|
||||||
snippets.hpp
|
snippets.hpp
|
||||||
snippets.ipp
|
snippets.ipp
|
||||||
core_examples.cpp
|
|
||||||
core_snippets.cpp
|
core_snippets.cpp
|
||||||
core_1_refresher.cpp
|
core_1_refresher.cpp
|
||||||
core_3_layers.cpp
|
core_3_layers.cpp
|
||||||
|
@ -19,7 +19,6 @@ alias run-tests :
|
|||||||
[ compile core_snippets.cpp ]
|
[ compile core_snippets.cpp ]
|
||||||
[ compile http_snippets.cpp ]
|
[ compile http_snippets.cpp ]
|
||||||
[ compile websocket_snippets.cpp ]
|
[ compile websocket_snippets.cpp ]
|
||||||
[ run core_examples.cpp $(TEST_MAIN) ]
|
|
||||||
[ run core_1_refresher.cpp $(TEST_MAIN) ]
|
[ run core_1_refresher.cpp $(TEST_MAIN) ]
|
||||||
[ run core_3_layers.cpp $(TEST_MAIN) ]
|
[ run core_3_layers.cpp $(TEST_MAIN) ]
|
||||||
[ run http_examples.cpp $(TEST_MAIN) ]
|
[ run http_examples.cpp $(TEST_MAIN) ]
|
||||||
@ -28,7 +27,6 @@ alias run-tests :
|
|||||||
|
|
||||||
exe fat-tests :
|
exe fat-tests :
|
||||||
$(TEST_MAIN)
|
$(TEST_MAIN)
|
||||||
core_examples.cpp
|
|
||||||
core_1_refresher.cpp
|
core_1_refresher.cpp
|
||||||
core_3_layers.cpp
|
core_3_layers.cpp
|
||||||
http_examples.cpp
|
http_examples.cpp
|
||||||
|
@ -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 <boost/beast/core/flat_buffer.hpp>
|
|
||||||
#include <boost/beast/core/ostream.hpp>
|
|
||||||
#include <boost/beast/_experimental/test/stream.hpp>
|
|
||||||
#include <boost/beast/test/yield_to.hpp>
|
|
||||||
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
|
||||||
|
|
||||||
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
|
|
@ -18,7 +18,6 @@ add_executable (tests-example-common
|
|||||||
${EXTRAS_FILES}
|
${EXTRAS_FILES}
|
||||||
${TEST_MAIN}
|
${TEST_MAIN}
|
||||||
Jamfile
|
Jamfile
|
||||||
detect_ssl.cpp
|
|
||||||
root_certificates.cpp
|
root_certificates.cpp
|
||||||
server_certificate.cpp
|
server_certificate.cpp
|
||||||
session_alloc.cpp
|
session_alloc.cpp
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
local SOURCES =
|
local SOURCES =
|
||||||
detect_ssl.cpp
|
|
||||||
root_certificates.cpp
|
root_certificates.cpp
|
||||||
server_certificate.cpp
|
server_certificate.cpp
|
||||||
session_alloc.cpp
|
session_alloc.cpp
|
||||||
|
@ -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"
|
|
||||||
|
|
Reference in New Issue
Block a user