mirror of
https://github.com/boostorg/beast.git
synced 2025-07-30 04:47:29 +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:
|
||||
|
||||
* websocket idle pings
|
||||
|
@ -95,6 +95,5 @@ effect:
|
||||
[include 4_buffers.qbk]
|
||||
[include 5_files.qbk]
|
||||
[include 6_composed.qbk]
|
||||
[include 7_detect_ssl.qbk]
|
||||
|
||||
[endsect]
|
||||
|
@ -8,7 +8,6 @@
|
||||
]
|
||||
|
||||
[section Writing Composed Operations]
|
||||
[block'''<?dbhtml stop-chunking?>''']
|
||||
|
||||
Asynchronous operations are started by calling a free function or member
|
||||
function known as an asynchronous ['__async_initfn__]. This function accepts
|
||||
@ -111,80 +110,7 @@ composed operations:
|
||||
]]
|
||||
]
|
||||
|
||||
|
||||
|
||||
[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]
|
||||
|
||||
|
||||
[include 6a_echo.qbk]
|
||||
[include 6b_detect_ssl.qbk]
|
||||
|
||||
[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.
|
||||
]
|
||||
|
||||
[import ../../example/common/detect_ssl.hpp]
|
||||
[import ../../example/doc/http_examples.hpp]
|
||||
[import ../../example/echo-op/echo_op.cpp]
|
||||
[import ../../example/http/client/sync/http_client_sync.cpp]
|
||||
@ -134,6 +133,7 @@
|
||||
[import ../../test/doc/core_3_layers.cpp]
|
||||
[import ../../test/doc/websocket_3_handshake.cpp]
|
||||
|
||||
[import ../../include/boost/beast/core/detect_ssl.hpp]
|
||||
[import ../../test/beast/core/rate_policy.cpp]
|
||||
|
||||
[section:quickref Reference]
|
||||
|
@ -64,10 +64,12 @@
|
||||
<bridgehead renderas="sect3">Functions</bridgehead>
|
||||
<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__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__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__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__get_lowest_layer">get_lowest_layer</link> <emphasis role="green">🞲</emphasis></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
|
||||
[@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 `tcp_stream`] offer:
|
||||
* Timeouts:
|
||||
@ -47,8 +54,9 @@
|
||||
[link beast.ref.boost__beast__unlimited_rate_policy `unlimited`],
|
||||
or a user-defined
|
||||
[link beast.concepts.RatePolicy ['RatePolicy]]!
|
||||
* [[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html P1322R0]]
|
||||
Put the strand on the socket itself, no more `bind_executor` at call sites!
|
||||
* Put the strand directly on the socket using
|
||||
[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html P1322R0],
|
||||
no more `bind_executor` at call sites!
|
||||
* Base classes
|
||||
[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`]
|
||||
@ -56,10 +64,7 @@
|
||||
* All asynchronous operations use Asio's
|
||||
[@boost:/doc/html/boost_asio/reference/async_initiate.html `async_initiate`]
|
||||
for efficient integration with Coroutines TS.
|
||||
* New [@../../example/websocket/server/chat-multi websocket-chat-multi]
|
||||
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
|
||||
* OpenSSL is now required to build tests and examples
|
||||
* See the full [link beast.release_notes [*Release Notes]] for a complete list
|
||||
of changes.
|
||||
|
||||
|
@ -14,7 +14,6 @@ if (OPENSSL_FOUND)
|
||||
|
||||
add_executable (advanced-server-flex
|
||||
${BOOST_BEAST_FILES}
|
||||
${PROJECT_SOURCE_DIR}/example/common/detect_ssl.hpp
|
||||
${PROJECT_SOURCE_DIR}/example/common/server_certificate.hpp
|
||||
Jamfile
|
||||
advanced_server_flex.cpp
|
||||
|
@ -13,7 +13,6 @@
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include "example/common/detect_ssl.hpp"
|
||||
#include "example/common/server_certificate.hpp"
|
||||
|
||||
#include <boost/beast/core.hpp>
|
||||
@ -822,7 +821,7 @@ public:
|
||||
// Set the timeout.
|
||||
stream_.expires_after(std::chrono::seconds(30));
|
||||
|
||||
async_detect_ssl(
|
||||
beast::async_detect_ssl(
|
||||
stream_,
|
||||
buffer_,
|
||||
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>
|
||||
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
|
||||
//
|
||||
template<
|
||||
@ -153,12 +158,6 @@ async_echo(
|
||||
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
|
||||
// stateful completion handler. We derive from `async_op_base` 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
|
||||
// call followed by invocation of operator(). We use BOOST_ASIO_HANDLER_TYPE
|
||||
// to convert the completion token into the correct handler type,
|
||||
@ -332,6 +328,9 @@ async_echo(
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
// Including this file undefines the macros used by the stackless fauxroutines.
|
||||
#include <boost/asio/unyield.hpp>
|
||||
|
||||
//]
|
||||
|
||||
struct move_only_handler
|
||||
|
@ -14,7 +14,6 @@ if (OPENSSL_FOUND)
|
||||
|
||||
add_executable (http-server-flex
|
||||
${BOOST_BEAST_FILES}
|
||||
${PROJECT_SOURCE_DIR}/example/common/detect_ssl.hpp
|
||||
${PROJECT_SOURCE_DIR}/example/common/server_certificate.hpp
|
||||
Jamfile
|
||||
http_server_flex.cpp
|
||||
|
@ -13,7 +13,6 @@
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include "example/common/detect_ssl.hpp"
|
||||
#include "example/common/server_certificate.hpp"
|
||||
|
||||
#include <boost/beast/core.hpp>
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/core/exchange.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
@ -140,6 +141,46 @@ fail_handler(error_code ec) noexcept
|
||||
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
|
||||
} // beast
|
||||
} // boost
|
||||
|
@ -21,46 +21,6 @@ namespace boost {
|
||||
namespace beast {
|
||||
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.
|
||||
*/
|
||||
template<class Executor>
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <boost/beast/core/buffers_range.hpp>
|
||||
#include <boost/beast/core/buffers_suffix.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/error.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_suffix.cpp
|
||||
buffers_to_string.cpp
|
||||
detect_ssl.cpp
|
||||
dynamic_buffer_ref.cpp
|
||||
error.cpp
|
||||
file.cpp
|
||||
|
@ -32,6 +32,7 @@ local SOURCES =
|
||||
buffers_range.cpp
|
||||
buffers_suffix.cpp
|
||||
buffers_to_string.cpp
|
||||
detect_ssl.cpp
|
||||
dynamic_buffer_ref.cpp
|
||||
error.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
|
||||
snippets.hpp
|
||||
snippets.ipp
|
||||
core_examples.cpp
|
||||
core_snippets.cpp
|
||||
core_1_refresher.cpp
|
||||
core_3_layers.cpp
|
||||
|
@ -19,7 +19,6 @@ alias run-tests :
|
||||
[ compile core_snippets.cpp ]
|
||||
[ compile http_snippets.cpp ]
|
||||
[ compile websocket_snippets.cpp ]
|
||||
[ run core_examples.cpp $(TEST_MAIN) ]
|
||||
[ run core_1_refresher.cpp $(TEST_MAIN) ]
|
||||
[ run core_3_layers.cpp $(TEST_MAIN) ]
|
||||
[ run http_examples.cpp $(TEST_MAIN) ]
|
||||
@ -28,7 +27,6 @@ alias run-tests :
|
||||
|
||||
exe fat-tests :
|
||||
$(TEST_MAIN)
|
||||
core_examples.cpp
|
||||
core_1_refresher.cpp
|
||||
core_3_layers.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}
|
||||
${TEST_MAIN}
|
||||
Jamfile
|
||||
detect_ssl.cpp
|
||||
root_certificates.cpp
|
||||
server_certificate.cpp
|
||||
session_alloc.cpp
|
||||
|
@ -8,7 +8,6 @@
|
||||
#
|
||||
|
||||
local SOURCES =
|
||||
detect_ssl.cpp
|
||||
root_certificates.cpp
|
||||
server_certificate.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