Support handshake with re-use of data already read from the wire.

Add new overloads of the SSL stream's handshake() and async_handshake()
functions, that accepts a ConstBufferSequence to be used as initial
input to the ssl engine for the handshake procedure.

Thanks go to Nick Jones <nick dot fa dot jones at gmail dot com>, on
whose work this commit is partially based.
This commit is contained in:
Christopher Kohlhoff 2013-03-22 22:25:18 +11:00
parent dd2accf95f
commit cea683f915
8 changed files with 333 additions and 8 deletions

View File

@ -320,6 +320,7 @@ nobase_include_HEADERS = \
asio/ssl/context_base.hpp \
asio/ssl/context.hpp \
asio/ssl/context_service.hpp \
asio/ssl/detail/buffered_handshake_op.hpp \
asio/ssl/detail/engine.hpp \
asio/ssl/detail/handshake_op.hpp \
asio/ssl/detail/impl/engine.ipp \

View File

@ -368,6 +368,33 @@ struct handler_type_requirements
asio::detail::lvref<const asio::error_code>()), \
char(0))>
#define ASIO_BUFFERED_HANDSHAKE_HANDLER_CHECK( \
handler_type, handler) \
\
typedef ASIO_HANDLER_TYPE(handler_type, \
void(asio::error_code, std::size_t)) \
asio_true_handler_type; \
\
ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \
sizeof(asio::detail::two_arg_handler_test( \
asio::detail::clvref< \
asio_true_handler_type>(), \
static_cast<const asio::error_code*>(0), \
static_cast<const std::size_t*>(0))) == 1, \
"BufferedHandshakeHandler type requirements not met") \
\
typedef asio::detail::handler_type_requirements< \
sizeof( \
asio::detail::argbyv( \
asio::detail::clvref< \
asio_true_handler_type>())) + \
sizeof( \
asio::detail::lvref< \
asio_true_handler_type>()( \
asio::detail::lvref<const asio::error_code>(), \
asio::detail::lvref<const std::size_t>()), \
char(0))>
#define ASIO_SHUTDOWN_HANDLER_CHECK( \
handler_type, handler) \
\
@ -435,6 +462,10 @@ struct handler_type_requirements
handler_type, handler) \
typedef int
#define ASIO_BUFFERED_HANDSHAKE_HANDLER_CHECK( \
handler_type, handler) \
typedef int
#define ASIO_SHUTDOWN_HANDLER_CHECK( \
handler_type, handler) \
typedef int

View File

@ -0,0 +1,110 @@
//
// ssl/detail/buffered_handshake_op.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2012 Christopher M. Kohlhoff (chris at kohlhoff 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)
//
#ifndef ASIO_SSL_DETAIL_BUFFERED_HANDSHAKE_OP_HPP
#define ASIO_SSL_DETAIL_BUFFERED_HANDSHAKE_OP_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#if !defined(ASIO_ENABLE_OLD_SSL)
# include "asio/ssl/detail/engine.hpp"
#endif // !defined(ASIO_ENABLE_OLD_SSL)
#include "asio/detail/push_options.hpp"
namespace asio {
namespace ssl {
namespace detail {
#if !defined(ASIO_ENABLE_OLD_SSL)
template <typename ConstBufferSequence>
class buffered_handshake_op
{
public:
buffered_handshake_op(stream_base::handshake_type type,
const ConstBufferSequence& buffers)
: type_(type),
buffers_(buffers),
total_buffer_size_(asio::buffer_size(buffers_))
{
}
engine::want operator()(engine& eng,
asio::error_code& ec,
std::size_t& bytes_transferred) const
{
typename ConstBufferSequence::const_iterator iter = buffers_.begin();
typename ConstBufferSequence::const_iterator end = buffers_.end();
std::size_t accumulated_size = 0;
for (;;)
{
engine::want want = eng.handshake(type_, ec);
if (want != engine::want_input_and_retry
|| bytes_transferred == total_buffer_size_)
return want;
// Find the next buffer piece to be fed to the engine.
while (iter != end)
{
const_buffer buffer(*iter);
// Skip over any buffers which have already been consumed by the engine.
if (bytes_transferred >= accumulated_size + buffer_size(buffer))
{
accumulated_size += buffer_size(buffer);
++iter;
continue;
}
// The current buffer may have been partially consumed by the engine on
// a previous iteration. If so, adjust the buffer to point to the
// unused portion.
if (bytes_transferred > accumulated_size)
buffer = buffer + (bytes_transferred - accumulated_size);
// Pass the buffer to the engine, and update the bytes transferred to
// reflect the total number of bytes consumed so far.
bytes_transferred += buffer_size(buffer);
buffer = eng.put_input(buffer);
bytes_transferred -= buffer_size(buffer);
break;
}
}
}
template <typename Handler>
void call_handler(Handler& handler,
const asio::error_code& ec,
const std::size_t& bytes_transferred) const
{
handler(ec, bytes_transferred);
}
private:
stream_base::handshake_type type_;
ConstBufferSequence buffers_;
std::size_t total_buffer_size_;
};
#endif // !defined(ASIO_ENABLE_OLD_SSL)
} // namespace detail
} // namespace ssl
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_SSL_DETAIL_BUFFERED_HANDSHAKE_OP_HPP

View File

@ -26,6 +26,7 @@
# include "asio/detail/noncopyable.hpp"
# include "asio/detail/type_traits.hpp"
# include "asio/ssl/context.hpp"
# include "asio/ssl/detail/buffered_handshake_op.hpp"
# include "asio/ssl/detail/handshake_op.hpp"
# include "asio/ssl/detail/io.hpp"
# include "asio/ssl/detail/read_op.hpp"
@ -343,6 +344,47 @@ public:
return ec;
}
/// Perform SSL handshaking.
/**
* This function is used to perform SSL handshaking on the stream. The
* function call will block until handshaking is complete or an error occurs.
*
* @param type The type of handshaking to be performed, i.e. as a client or as
* a server.
*
* @param buffers The buffered data to be reused for the handshake.
*
* @throws asio::system_error Thrown on failure.
*/
template <typename ConstBufferSequence>
void handshake(handshake_type type, const ConstBufferSequence& buffers)
{
asio::error_code ec;
handshake(type, buffers, ec);
asio::detail::throw_error(ec, "handshake");
}
/// Perform SSL handshaking.
/**
* This function is used to perform SSL handshaking on the stream. The
* function call will block until handshaking is complete or an error occurs.
*
* @param type The type of handshaking to be performed, i.e. as a client or as
* a server.
*
* @param buffers The buffered data to be reused for the handshake.
*
* @param ec Set to indicate what error occurred, if any.
*/
template <typename ConstBufferSequence>
asio::error_code handshake(handshake_type type,
const ConstBufferSequence& buffers, asio::error_code& ec)
{
detail::io(next_layer_, core_,
detail::buffered_handshake_op<ConstBufferSequence>(type, buffers), ec);
return ec;
}
/// Start an asynchronous SSL handshake.
/**
* This function is used to asynchronously perform an SSL handshake on the
@ -378,6 +420,49 @@ public:
return init.result.get();
}
/// Start an asynchronous SSL handshake.
/**
* This function is used to asynchronously perform an SSL handshake on the
* stream. This function call always returns immediately.
*
* @param type The type of handshaking to be performed, i.e. as a client or as
* a server.
*
* @param buffers The buffered data to be reused for the handshake. Although
* the buffers object may be copied as necessary, ownership of the underlying
* buffers is retained by the caller, which must guarantee that they remain
* valid until the handler is called.
*
* @param handler The handler to be called when the handshake operation
* completes. Copies will be made of the handler as required. The equivalent
* function signature of the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Amount of buffers used in handshake.
* ); @endcode
*/
template <typename ConstBufferSequence, typename BufferedHandshakeHandler>
ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler,
void (asio::error_code, std::size_t))
async_handshake(handshake_type type, const ConstBufferSequence& buffers,
ASIO_MOVE_ARG(BufferedHandshakeHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a BufferedHandshakeHandler.
ASIO_BUFFERED_HANDSHAKE_HANDLER_CHECK(
BufferedHandshakeHandler, handler) type_check;
asio::detail::async_result_init<BufferedHandshakeHandler,
void (asio::error_code, std::size_t)> init(
ASIO_MOVE_CAST(BufferedHandshakeHandler)(handler));
detail::async_io(next_layer_, core_,
detail::buffered_handshake_op<ConstBufferSequence>(type, buffers),
init.handler);
return init.result.get();
}
/// Shut down SSL on the stream.
/**
* This function is used to shut down SSL on the stream. The function call

View File

@ -317,6 +317,7 @@
</simplelist>
<bridgehead renderas="sect3">Type Requirements</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="asio.reference.BufferedHandshakeHandler">BufferedHandshakeHandler</link></member>
<member><link linkend="asio.reference.HandshakeHandler">HandshakeHandler</link></member>
<member><link linkend="asio.reference.ShutdownHandler">ShutdownHandler</link></member>
</simplelist>

View File

@ -42,6 +42,7 @@
[include requirements/AsyncRandomAccessWriteDevice.qbk]
[include requirements/AsyncReadStream.qbk]
[include requirements/AsyncWriteStream.qbk]
[include requirements/BufferedHandshakeHandler.qbk]
[include requirements/CompletionHandler.qbk]
[include requirements/ComposedConnectHandler.qbk]
[include requirements/ConnectHandler.qbk]

View File

@ -0,0 +1,54 @@
[/
/ Copyright (c) 2003-2012 Christopher M. Kohlhoff (chris at kohlhoff 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)
/]
[section:BufferedHandshakeHandler Buffered handshake handler requirements]
A buffered handshake handler must meet the requirements for a [link
asio.reference.Handler handler]. A value `h` of a buffered handshake handler
class should work correctly in the expression `h(ec, s)`, where `ec` is an
lvalue of type `const error_code` and `s` is an lvalue of type `const size_t`.
[heading Examples]
A free function as a buffered handshake handler:
void handshake_handler(
const asio::error_code& ec,
std::size_t bytes_transferred)
{
...
}
A buffered handshake handler function object:
struct handshake_handler
{
...
void operator()(
const asio::error_code& ec,
std::size_t bytes_transferred)
{
...
}
...
};
A non-static class member function adapted to a buffered handshake handler using `bind()`:
void my_class::handshake_handler(
const asio::error_code& ec,
std::size_t bytes_transferred)
{
...
}
...
socket.async_handshake(...,
boost::bind(&my_class::handshake_handler,
this, asio::placeholders::error,
asio::placeholders::bytes_transferred));
[endsect]

View File

@ -41,6 +41,10 @@ void handshake_handler(const asio::error_code&)
{
}
void buffered_handshake_handler(const asio::error_code&, std::size_t)
{
}
void shutdown_handler(const asio::error_code&)
{
}
@ -110,6 +114,21 @@ void test()
stream1.handshake(ssl::stream_base::client, ec);
stream1.handshake(ssl::stream_base::server, ec);
#if !defined(ASIO_ENABLE_OLD_SSL)
stream1.handshake(ssl::stream_base::client, buffer(mutable_char_buffer));
stream1.handshake(ssl::stream_base::server, buffer(mutable_char_buffer));
stream1.handshake(ssl::stream_base::client, buffer(const_char_buffer));
stream1.handshake(ssl::stream_base::server, buffer(const_char_buffer));
stream1.handshake(ssl::stream_base::client,
buffer(mutable_char_buffer), ec);
stream1.handshake(ssl::stream_base::server,
buffer(mutable_char_buffer), ec);
stream1.handshake(ssl::stream_base::client,
buffer(const_char_buffer), ec);
stream1.handshake(ssl::stream_base::server,
buffer(const_char_buffer), ec);
#endif // !defined(ASIO_ENABLE_OLD_SSL)
stream1.async_handshake(ssl::stream_base::client, handshake_handler);
stream1.async_handshake(ssl::stream_base::server, handshake_handler);
int i1 = stream1.async_handshake(ssl::stream_base::client, lazy);
@ -117,12 +136,35 @@ void test()
int i2 = stream1.async_handshake(ssl::stream_base::server, lazy);
(void)i2;
#if !defined(ASIO_ENABLE_OLD_SSL)
stream1.async_handshake(ssl::stream_base::client,
buffer(mutable_char_buffer), buffered_handshake_handler);
stream1.async_handshake(ssl::stream_base::server,
buffer(mutable_char_buffer), buffered_handshake_handler);
stream1.async_handshake(ssl::stream_base::client,
buffer(const_char_buffer), buffered_handshake_handler);
stream1.async_handshake(ssl::stream_base::server,
buffer(const_char_buffer), buffered_handshake_handler);
int i3 = stream1.async_handshake(ssl::stream_base::client,
buffer(mutable_char_buffer), lazy);
(void)i3;
int i4 = stream1.async_handshake(ssl::stream_base::server,
buffer(mutable_char_buffer), lazy);
(void)i4;
int i5 = stream1.async_handshake(ssl::stream_base::client,
buffer(const_char_buffer), lazy);
(void)i5;
int i6 = stream1.async_handshake(ssl::stream_base::server,
buffer(const_char_buffer), lazy);
(void)i6;
#endif // !defined(ASIO_ENABLE_OLD_SSL)
stream1.shutdown();
stream1.shutdown(ec);
stream1.async_shutdown(shutdown_handler);
int i3 = stream1.async_shutdown(lazy);
(void)i3;
int i7 = stream1.async_shutdown(lazy);
(void)i7;
stream1.write_some(buffer(mutable_char_buffer));
stream1.write_some(buffer(const_char_buffer));
@ -131,17 +173,17 @@ void test()
stream1.async_write_some(buffer(mutable_char_buffer), write_some_handler);
stream1.async_write_some(buffer(const_char_buffer), write_some_handler);
int i4 = stream1.async_write_some(buffer(mutable_char_buffer), lazy);
(void)i4;
int i5 = stream1.async_write_some(buffer(const_char_buffer), lazy);
(void)i5;
int i8 = stream1.async_write_some(buffer(mutable_char_buffer), lazy);
(void)i8;
int i9 = stream1.async_write_some(buffer(const_char_buffer), lazy);
(void)i9;
stream1.read_some(buffer(mutable_char_buffer));
stream1.read_some(buffer(mutable_char_buffer), ec);
stream1.async_read_some(buffer(mutable_char_buffer), read_some_handler);
int i6 = stream1.async_read_some(buffer(mutable_char_buffer), lazy);
(void)i6;
int i10 = stream1.async_read_some(buffer(mutable_char_buffer), lazy);
(void)i10;
#if defined(ASIO_ENABLE_OLD_SSL)
stream1.peek(buffer(mutable_char_buffer));