forked from boostorg/beast
Add flat_stream to experimental:
This adds a new stream wrapper class template designed to address a performance shortcoming of boost::asio::ssl::stream.
This commit is contained in:
@@ -1,3 +1,9 @@
|
|||||||
|
Version 170:
|
||||||
|
|
||||||
|
* Add flat_stream to experimental
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
Version 169:
|
Version 169:
|
||||||
|
|
||||||
* Use buffers_to_string in tests
|
* Use buffers_to_string in tests
|
||||||
|
@@ -216,16 +216,6 @@ listed here along with a description of their use:
|
|||||||
in the documentation. It is used by the "flex" servers which
|
in the documentation. It is used by the "flex" servers which
|
||||||
support both plain and SSL sessions on the same port.
|
support both plain and SSL sessions on the same port.
|
||||||
]
|
]
|
||||||
][
|
|
||||||
[[source_file example/common/flat_stream.hpp]]
|
|
||||||
[
|
|
||||||
This wrapper flattens buffer sequences having length greater than 1
|
|
||||||
and total size below a predefined amount, using a dynamic memory
|
|
||||||
allocation. It is primarily designed to overcome a performance
|
|
||||||
limitation of the current Boost.Asio version of `ssl::stream`, which
|
|
||||||
does not use OpenSSL's scatter/gather interface for its primitive
|
|
||||||
read and write operations.
|
|
||||||
]
|
|
||||||
][
|
][
|
||||||
[[source_file example/common/root_certificates.hpp]]
|
[[source_file example/common/root_certificates.hpp]]
|
||||||
[
|
[
|
||||||
|
@@ -284,6 +284,26 @@
|
|||||||
</row>
|
</row>
|
||||||
</tbody>
|
</tbody>
|
||||||
</tgroup>
|
</tgroup>
|
||||||
|
<tgroup cols="1">
|
||||||
|
<colspec colname="a"/>
|
||||||
|
<thead>
|
||||||
|
<row>
|
||||||
|
<entry valign="center" namest="a" nameend="c">
|
||||||
|
<bridgehead renderas="sect2">Experimental</bridgehead>
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<row>
|
||||||
|
<entry valign="top">
|
||||||
|
<bridgehead renderas="sect3">Classes</bridgehead>
|
||||||
|
<simplelist type="vert" columns="1">
|
||||||
|
<member><link linkend="beast.ref.boost__beast__flat_stream">flat_stream</link></member>
|
||||||
|
</simplelist>
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
</tbody>
|
||||||
|
</tgroup>
|
||||||
<!--
|
<!--
|
||||||
<tgroup cols="1">
|
<tgroup cols="1">
|
||||||
<colspec colname="a"/>
|
<colspec colname="a"/>
|
||||||
|
@@ -105,6 +105,7 @@ WARN_LOGFILE =
|
|||||||
INPUT = \
|
INPUT = \
|
||||||
$(LIB_DIR)/include/boost/beast/ \
|
$(LIB_DIR)/include/boost/beast/ \
|
||||||
$(LIB_DIR)/include/boost/beast/core \
|
$(LIB_DIR)/include/boost/beast/core \
|
||||||
|
$(LIB_DIR)/include/boost/beast/experimental/core \
|
||||||
$(LIB_DIR)/include/boost/beast/http \
|
$(LIB_DIR)/include/boost/beast/http \
|
||||||
$(LIB_DIR)/include/boost/beast/websocket \
|
$(LIB_DIR)/include/boost/beast/websocket \
|
||||||
$(LIB_DIR)/include/boost/beast/zlib
|
$(LIB_DIR)/include/boost/beast/zlib
|
||||||
|
@@ -15,7 +15,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/detect_ssl.hpp
|
||||||
${PROJECT_SOURCE_DIR}/example/common/flat_stream.hpp
|
|
||||||
${PROJECT_SOURCE_DIR}/example/common/server_certificate.hpp
|
${PROJECT_SOURCE_DIR}/example/common/server_certificate.hpp
|
||||||
${PROJECT_SOURCE_DIR}/example/common/ssl_stream.hpp
|
${PROJECT_SOURCE_DIR}/example/common/ssl_stream.hpp
|
||||||
Jamfile
|
Jamfile
|
||||||
|
@@ -1,506 +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
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BOOST_BEAST_EXAMPLE_COMMON_FLAT_STREAM_HPP
|
|
||||||
#define BOOST_BEAST_EXAMPLE_COMMON_FLAT_STREAM_HPP
|
|
||||||
|
|
||||||
#include <boost/beast/core/buffers_prefix.hpp>
|
|
||||||
#include <boost/beast/core/error.hpp>
|
|
||||||
#include <boost/beast/core/handler_ptr.hpp>
|
|
||||||
#include <boost/beast/core/type_traits.hpp>
|
|
||||||
#include <boost/beast/websocket/teardown.hpp>
|
|
||||||
#include <boost/asio/associated_allocator.hpp>
|
|
||||||
#include <boost/asio/associated_executor.hpp>
|
|
||||||
#include <boost/asio/buffer.hpp>
|
|
||||||
#include <boost/asio/coroutine.hpp>
|
|
||||||
#include <boost/asio/executor_work_guard.hpp>
|
|
||||||
#include <boost/asio/handler_continuation_hook.hpp>
|
|
||||||
#include <boost/asio/handler_invoke_hook.hpp>
|
|
||||||
#include <boost/throw_exception.hpp>
|
|
||||||
#include <iterator>
|
|
||||||
#include <memory>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
class flat_stream_base
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static std::size_t constexpr coalesce_limit = 64 * 1024;
|
|
||||||
|
|
||||||
// calculates the coalesce settings for a buffer sequence
|
|
||||||
template<class BufferSequence>
|
|
||||||
static
|
|
||||||
std::pair<std::size_t, bool>
|
|
||||||
coalesce(BufferSequence const& buffers, std::size_t limit)
|
|
||||||
{
|
|
||||||
std::pair<std::size_t, bool> result{0, false};
|
|
||||||
auto first = boost::asio::buffer_sequence_begin(buffers);
|
|
||||||
auto last = boost::asio::buffer_sequence_end(buffers);
|
|
||||||
if(first != last)
|
|
||||||
{
|
|
||||||
result.first = boost::asio::buffer_size(*first);
|
|
||||||
if(result.first < limit)
|
|
||||||
{
|
|
||||||
auto it = first;
|
|
||||||
auto prev = first;
|
|
||||||
while(++it != last)
|
|
||||||
{
|
|
||||||
auto const n =
|
|
||||||
boost::asio::buffer_size(*it);
|
|
||||||
if(result.first + n > limit)
|
|
||||||
break;
|
|
||||||
result.first += n;
|
|
||||||
prev = it;
|
|
||||||
}
|
|
||||||
result.second = prev != first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // detail
|
|
||||||
|
|
||||||
/** Flat stream wrapper.
|
|
||||||
|
|
||||||
This wrapper flattens buffer sequences having length greater than 1
|
|
||||||
and total size below a predefined amount, using a dynamic memory
|
|
||||||
allocation. It is primarily designed to overcome a performance
|
|
||||||
limitation of the current Boost.Asio version of ssl::stream, which
|
|
||||||
does not use OpenSSL's scatter/gather interface for its primitive
|
|
||||||
read and write operations.
|
|
||||||
|
|
||||||
See:
|
|
||||||
https://github.com/boostorg/beast/issues/1108
|
|
||||||
https://github.com/boostorg/asio/issues/100
|
|
||||||
https://stackoverflow.com/questions/38198638/openssl-ssl-write-from-multiple-buffers-ssl-writev
|
|
||||||
https://stackoverflow.com/questions/50026167/performance-drop-on-port-from-beast-1-0-0-b66-to-boost-1-67-0-beast
|
|
||||||
*/
|
|
||||||
template<class NextLayer>
|
|
||||||
class flat_stream : private detail::flat_stream_base
|
|
||||||
{
|
|
||||||
// Largest buffer size we will flatten
|
|
||||||
static std::size_t constexpr max_size = 1024 * 1024;
|
|
||||||
|
|
||||||
template<class, class> class write_op;
|
|
||||||
|
|
||||||
NextLayer stream_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// The type of the next layer.
|
|
||||||
using next_layer_type =
|
|
||||||
typename std::remove_reference<NextLayer>::type;
|
|
||||||
|
|
||||||
/// The type of the lowest layer.
|
|
||||||
using lowest_layer_type = boost::beast::get_lowest_layer<next_layer_type>;
|
|
||||||
|
|
||||||
/// The type of the executor associated with the object.
|
|
||||||
using executor_type = typename next_layer_type::executor_type;
|
|
||||||
|
|
||||||
~flat_stream() = default;
|
|
||||||
flat_stream(flat_stream&&) = default;
|
|
||||||
flat_stream(flat_stream const&) = delete;
|
|
||||||
flat_stream& operator=(flat_stream&&) = default;
|
|
||||||
flat_stream& operator=(flat_stream const&) = delete;
|
|
||||||
|
|
||||||
/** Constructor
|
|
||||||
|
|
||||||
Arguments, if any, are forwarded to the next layer's constructor.
|
|
||||||
*/
|
|
||||||
template<class... Args>
|
|
||||||
explicit
|
|
||||||
flat_stream(Args&&... args);
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/** Get the executor associated with the object.
|
|
||||||
|
|
||||||
This function may be used to obtain the executor object that the
|
|
||||||
stream uses to dispatch handlers for asynchronous operations.
|
|
||||||
|
|
||||||
@return A copy of the executor that stream will use to dispatch handlers.
|
|
||||||
*/
|
|
||||||
executor_type
|
|
||||||
get_executor() noexcept
|
|
||||||
{
|
|
||||||
return stream_.get_executor();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get a reference to the next layer
|
|
||||||
|
|
||||||
This function returns a reference to the next layer
|
|
||||||
in a stack of stream layers.
|
|
||||||
|
|
||||||
@return A reference to the next layer in the stack of
|
|
||||||
stream layers.
|
|
||||||
*/
|
|
||||||
next_layer_type&
|
|
||||||
next_layer()
|
|
||||||
{
|
|
||||||
return stream_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get a reference to the next layer
|
|
||||||
|
|
||||||
This function returns a reference to the next layer in a
|
|
||||||
stack of stream layers.
|
|
||||||
|
|
||||||
@return A reference to the next layer in the stack of
|
|
||||||
stream layers.
|
|
||||||
*/
|
|
||||||
next_layer_type const&
|
|
||||||
next_layer() const
|
|
||||||
{
|
|
||||||
return stream_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get a reference to the lowest layer
|
|
||||||
|
|
||||||
This function returns a reference to the lowest layer
|
|
||||||
in a stack of stream layers.
|
|
||||||
|
|
||||||
@return A reference to the lowest layer in the stack of
|
|
||||||
stream layers.
|
|
||||||
*/
|
|
||||||
lowest_layer_type&
|
|
||||||
lowest_layer()
|
|
||||||
{
|
|
||||||
return stream_.lowest_layer();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get a reference to the lowest layer
|
|
||||||
|
|
||||||
This function returns a reference to the lowest layer
|
|
||||||
in a stack of stream layers.
|
|
||||||
|
|
||||||
@return A reference to the lowest layer in the stack of
|
|
||||||
stream layers. Ownership is not transferred to the caller.
|
|
||||||
*/
|
|
||||||
lowest_layer_type const&
|
|
||||||
lowest_layer() const
|
|
||||||
{
|
|
||||||
return stream_.lowest_layer();
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
|
||||||
|
|
||||||
template<class MutableBufferSequence>
|
|
||||||
std::size_t
|
|
||||||
read_some(MutableBufferSequence const& buffers);
|
|
||||||
|
|
||||||
template<class MutableBufferSequence>
|
|
||||||
std::size_t
|
|
||||||
read_some(
|
|
||||||
MutableBufferSequence const& buffers,
|
|
||||||
boost::system::error_code& ec);
|
|
||||||
|
|
||||||
template<class ConstBufferSequence>
|
|
||||||
std::size_t
|
|
||||||
write_some(ConstBufferSequence const& buffers);
|
|
||||||
|
|
||||||
template<class ConstBufferSequence>
|
|
||||||
std::size_t
|
|
||||||
write_some(
|
|
||||||
ConstBufferSequence const& buffers,
|
|
||||||
boost::system::error_code& ec);
|
|
||||||
|
|
||||||
template<
|
|
||||||
class MutableBufferSequence,
|
|
||||||
class ReadHandler>
|
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
|
||||||
ReadHandler, void(boost::system::error_code, std::size_t))
|
|
||||||
async_read_some(
|
|
||||||
MutableBufferSequence const& buffers,
|
|
||||||
ReadHandler&& handler);
|
|
||||||
|
|
||||||
template<
|
|
||||||
class ConstBufferSequence,
|
|
||||||
class WriteHandler>
|
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
|
||||||
WriteHandler, void(boost::system::error_code, std::size_t))
|
|
||||||
async_write_some(
|
|
||||||
ConstBufferSequence const& buffers,
|
|
||||||
WriteHandler&& handler);
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
friend
|
|
||||||
void
|
|
||||||
teardown(boost::beast::websocket::role_type,
|
|
||||||
flat_stream<T>& s, boost::system::error_code& ec);
|
|
||||||
|
|
||||||
template<class T, class TeardownHandler>
|
|
||||||
friend
|
|
||||||
void
|
|
||||||
async_teardown(boost::beast::websocket::role_type role,
|
|
||||||
flat_stream<T>& s, TeardownHandler&& handler);
|
|
||||||
};
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class ConstBufferSequence, class Handler>
|
|
||||||
class flat_stream<NextLayer>::write_op
|
|
||||||
: public boost::asio::coroutine
|
|
||||||
{
|
|
||||||
using alloc_type = typename
|
|
||||||
boost::asio::associated_allocator_t<Handler>::template
|
|
||||||
rebind<char>::other;
|
|
||||||
|
|
||||||
struct deleter
|
|
||||||
{
|
|
||||||
alloc_type alloc;
|
|
||||||
std::size_t size = 0;
|
|
||||||
|
|
||||||
explicit
|
|
||||||
deleter(alloc_type const& alloc_)
|
|
||||||
: alloc(alloc_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
operator()(char* p)
|
|
||||||
{
|
|
||||||
alloc.deallocate(p, size);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
flat_stream<NextLayer>& s_;
|
|
||||||
ConstBufferSequence b_;
|
|
||||||
std::unique_ptr<char, deleter> p_;
|
|
||||||
Handler h_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
template<class DeducedHandler>
|
|
||||||
write_op(
|
|
||||||
flat_stream<NextLayer>& s,
|
|
||||||
ConstBufferSequence const& b,
|
|
||||||
DeducedHandler&& h)
|
|
||||||
: s_(s)
|
|
||||||
, b_(b)
|
|
||||||
, p_(nullptr, deleter{
|
|
||||||
(boost::asio::get_associated_allocator)(h)})
|
|
||||||
, h_(std::forward<DeducedHandler>(h))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
using allocator_type =
|
|
||||||
boost::asio::associated_allocator_t<Handler>;
|
|
||||||
|
|
||||||
allocator_type
|
|
||||||
get_allocator() const noexcept
|
|
||||||
{
|
|
||||||
return (boost::asio::get_associated_allocator)(h_);
|
|
||||||
}
|
|
||||||
|
|
||||||
using executor_type = boost::asio::associated_executor_t<
|
|
||||||
Handler, decltype(std::declval<NextLayer&>().get_executor())>;
|
|
||||||
|
|
||||||
executor_type
|
|
||||||
get_executor() const noexcept
|
|
||||||
{
|
|
||||||
return (boost::asio::get_associated_executor)(
|
|
||||||
h_, s_.get_executor());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
operator()(
|
|
||||||
boost::system::error_code ec,
|
|
||||||
std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
BOOST_ASIO_CORO_REENTER(*this)
|
|
||||||
{
|
|
||||||
BOOST_ASIO_CORO_YIELD
|
|
||||||
{
|
|
||||||
auto const result = coalesce(b_, coalesce_limit);
|
|
||||||
if(result.second)
|
|
||||||
{
|
|
||||||
p_.get_deleter().size = result.first;
|
|
||||||
p_.reset(p_.get_deleter().alloc.allocate(
|
|
||||||
p_.get_deleter().size));
|
|
||||||
boost::asio::buffer_copy(
|
|
||||||
boost::asio::buffer(
|
|
||||||
p_.get(), p_.get_deleter().size),
|
|
||||||
b_, result.first);
|
|
||||||
s_.stream_.async_write_some(
|
|
||||||
boost::asio::buffer(
|
|
||||||
p_.get(), p_.get_deleter().size),
|
|
||||||
std::move(*this));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
s_.stream_.async_write_some(
|
|
||||||
boost::beast::buffers_prefix(result.first, b_),
|
|
||||||
std::move(*this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p_.reset();
|
|
||||||
h_(ec, bytes_transferred);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
friend
|
|
||||||
bool asio_handler_is_continuation(write_op* op)
|
|
||||||
{
|
|
||||||
using boost::asio::asio_handler_is_continuation;
|
|
||||||
return asio_handler_is_continuation(
|
|
||||||
std::addressof(op->h_));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Function>
|
|
||||||
friend
|
|
||||||
void asio_handler_invoke(Function&& f, write_op* op)
|
|
||||||
{
|
|
||||||
using boost::asio::asio_handler_invoke;
|
|
||||||
asio_handler_invoke(f, std::addressof(op->h_));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class... Args>
|
|
||||||
flat_stream<NextLayer>::
|
|
||||||
flat_stream(Args&&... args)
|
|
||||||
: stream_(std::forward<Args>(args)...)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class MutableBufferSequence>
|
|
||||||
std::size_t
|
|
||||||
flat_stream<NextLayer>::
|
|
||||||
read_some(MutableBufferSequence const& buffers)
|
|
||||||
{
|
|
||||||
static_assert(boost::beast::is_sync_read_stream<next_layer_type>::value,
|
|
||||||
"SyncReadStream requirements not met");
|
|
||||||
static_assert(boost::asio::is_mutable_buffer_sequence<
|
|
||||||
MutableBufferSequence>::value,
|
|
||||||
"MutableBufferSequence requirements not met");
|
|
||||||
boost::system::error_code ec;
|
|
||||||
auto n = read_some(buffers, ec);
|
|
||||||
if(ec)
|
|
||||||
BOOST_THROW_EXCEPTION(boost::system::system_error{ec});
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class MutableBufferSequence>
|
|
||||||
std::size_t
|
|
||||||
flat_stream<NextLayer>::
|
|
||||||
read_some(MutableBufferSequence const& buffers, boost::system::error_code& ec)
|
|
||||||
{
|
|
||||||
return stream_.read_some(buffers, ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class ConstBufferSequence>
|
|
||||||
std::size_t
|
|
||||||
flat_stream<NextLayer>::
|
|
||||||
write_some(ConstBufferSequence const& buffers)
|
|
||||||
{
|
|
||||||
static_assert(boost::beast::is_sync_write_stream<next_layer_type>::value,
|
|
||||||
"SyncWriteStream requirements not met");
|
|
||||||
static_assert(boost::asio::is_const_buffer_sequence<
|
|
||||||
ConstBufferSequence>::value,
|
|
||||||
"ConstBufferSequence requirements not met");
|
|
||||||
boost::system::error_code ec;
|
|
||||||
auto n = write_some(buffers, ec);
|
|
||||||
if(ec)
|
|
||||||
BOOST_THROW_EXCEPTION(boost::system::system_error{ec});
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class ConstBufferSequence>
|
|
||||||
std::size_t
|
|
||||||
flat_stream<NextLayer>::
|
|
||||||
write_some(
|
|
||||||
ConstBufferSequence const& buffers,
|
|
||||||
boost::system::error_code& ec)
|
|
||||||
{
|
|
||||||
auto const result = coalesce(buffers, coalesce_limit);
|
|
||||||
if(result.second)
|
|
||||||
{
|
|
||||||
std::unique_ptr<char[]> p{new char[result.first]};
|
|
||||||
auto const b = boost::asio::buffer(p.get(), result.first);
|
|
||||||
boost::asio::buffer_copy(b, buffers);
|
|
||||||
return stream_.write_some(b, ec);
|
|
||||||
}
|
|
||||||
return stream_.write_some(
|
|
||||||
boost::beast::buffers_prefix(result.first, buffers), ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<
|
|
||||||
class MutableBufferSequence,
|
|
||||||
class ReadHandler>
|
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
|
||||||
ReadHandler, void(boost::system::error_code, std::size_t))
|
|
||||||
flat_stream<NextLayer>::
|
|
||||||
async_read_some(
|
|
||||||
MutableBufferSequence const& buffers,
|
|
||||||
ReadHandler&& handler)
|
|
||||||
{
|
|
||||||
static_assert(boost::beast::is_async_read_stream<next_layer_type>::value,
|
|
||||||
"AsyncReadStream requirements not met");
|
|
||||||
static_assert(boost::asio::is_mutable_buffer_sequence<
|
|
||||||
MutableBufferSequence >::value,
|
|
||||||
"MutableBufferSequence requirements not met");
|
|
||||||
return stream_.async_read_some(
|
|
||||||
buffers, std::forward<ReadHandler>(handler));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<
|
|
||||||
class ConstBufferSequence,
|
|
||||||
class WriteHandler>
|
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
|
||||||
WriteHandler, void(boost::system::error_code, std::size_t))
|
|
||||||
flat_stream<NextLayer>::
|
|
||||||
async_write_some(
|
|
||||||
ConstBufferSequence const& buffers,
|
|
||||||
WriteHandler&& handler)
|
|
||||||
{
|
|
||||||
static_assert(boost::beast::is_async_write_stream<next_layer_type>::value,
|
|
||||||
"AsyncWriteStream requirements not met");
|
|
||||||
static_assert(boost::asio::is_const_buffer_sequence<
|
|
||||||
ConstBufferSequence>::value,
|
|
||||||
"ConstBufferSequence requirements not met");
|
|
||||||
BOOST_BEAST_HANDLER_INIT(
|
|
||||||
WriteHandler, void(boost::system::error_code, std::size_t));
|
|
||||||
write_op<ConstBufferSequence, BOOST_ASIO_HANDLER_TYPE(
|
|
||||||
WriteHandler, void(boost::system::error_code, std::size_t))>{
|
|
||||||
*this, buffers, std::move(init.completion_handler)}({}, 0);
|
|
||||||
return init.result.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
void
|
|
||||||
teardown(
|
|
||||||
boost::beast::websocket::role_type role,
|
|
||||||
flat_stream<NextLayer>& s,
|
|
||||||
boost::system::error_code& ec)
|
|
||||||
{
|
|
||||||
using boost::beast::websocket::teardown;
|
|
||||||
teardown(role, s.stream_, ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer, class TeardownHandler>
|
|
||||||
void
|
|
||||||
async_teardown(
|
|
||||||
boost::beast::websocket::role_type role,
|
|
||||||
flat_stream<NextLayer>& s,
|
|
||||||
TeardownHandler&& handler)
|
|
||||||
{
|
|
||||||
using boost::beast::websocket::async_teardown;
|
|
||||||
async_teardown(role, s.stream_, std::move(handler));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@@ -1,329 +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
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BOOST_BEAST_EXAMPLE_COMMON_SSL_STREAM_HPP
|
|
||||||
#define BOOST_BEAST_EXAMPLE_COMMON_SSL_STREAM_HPP
|
|
||||||
|
|
||||||
// This include is necessary to work with `ssl::stream` and `boost::beast::websocket::stream`
|
|
||||||
#include <boost/beast/websocket/ssl.hpp>
|
|
||||||
|
|
||||||
#include "flat_stream.hpp"
|
|
||||||
|
|
||||||
#include <boost/asio/ip/tcp.hpp>
|
|
||||||
#include <boost/asio/ssl/stream.hpp>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <memory>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
/** C++11 enabled SSL socket wrapper
|
|
||||||
|
|
||||||
This wrapper provides an interface identical to `boost::asio::ssl::stream`,
|
|
||||||
with the following additional properties:
|
|
||||||
|
|
||||||
@li Satisfies @b MoveConstructible
|
|
||||||
|
|
||||||
@li Satisfies @b MoveAssignable
|
|
||||||
|
|
||||||
@li Constructible from a moved socket.
|
|
||||||
*/
|
|
||||||
template<class NextLayer>
|
|
||||||
class ssl_stream
|
|
||||||
: public boost::asio::ssl::stream_base
|
|
||||||
{
|
|
||||||
using ssl_stream_type = boost::asio::ssl::stream<NextLayer>;
|
|
||||||
using stream_type = flat_stream<ssl_stream_type>;
|
|
||||||
|
|
||||||
std::unique_ptr<stream_type> p_;
|
|
||||||
boost::asio::ssl::context* ctx_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// The native handle type of the SSL stream.
|
|
||||||
using native_handle_type =
|
|
||||||
typename ssl_stream_type::native_handle_type;
|
|
||||||
|
|
||||||
/// Structure for use with deprecated impl_type.
|
|
||||||
using impl_struct = typename ssl_stream_type::impl_struct;
|
|
||||||
|
|
||||||
/// The type of the next layer.
|
|
||||||
using next_layer_type = typename ssl_stream_type::next_layer_type;
|
|
||||||
|
|
||||||
/// The type of the lowest layer.
|
|
||||||
using lowest_layer_type = typename ssl_stream_type::lowest_layer_type;
|
|
||||||
|
|
||||||
/// The type of the executor associated with the object.
|
|
||||||
using executor_type = typename stream_type::executor_type;
|
|
||||||
|
|
||||||
template<class Arg>
|
|
||||||
ssl_stream(
|
|
||||||
Arg&& arg,
|
|
||||||
boost::asio::ssl::context& ctx)
|
|
||||||
: p_(new stream_type{
|
|
||||||
std::forward<Arg>(arg), ctx})
|
|
||||||
, ctx_(&ctx)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ssl_stream(ssl_stream&& other)
|
|
||||||
: p_(std::move(other.p_))
|
|
||||||
, ctx_(other.ctx_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ssl_stream& operator=(ssl_stream&& other)
|
|
||||||
{
|
|
||||||
p_ = std::move(other.p_);
|
|
||||||
ctx_ = other.ctx_;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
executor_type
|
|
||||||
get_executor() noexcept
|
|
||||||
{
|
|
||||||
return p_->get_executor();
|
|
||||||
}
|
|
||||||
|
|
||||||
native_handle_type
|
|
||||||
native_handle()
|
|
||||||
{
|
|
||||||
return p_->next_layer().native_handle();
|
|
||||||
}
|
|
||||||
|
|
||||||
next_layer_type const&
|
|
||||||
next_layer() const
|
|
||||||
{
|
|
||||||
return p_->next_layer().next_layer();
|
|
||||||
}
|
|
||||||
|
|
||||||
next_layer_type&
|
|
||||||
next_layer()
|
|
||||||
{
|
|
||||||
return p_->next_layer().next_layer();
|
|
||||||
}
|
|
||||||
|
|
||||||
lowest_layer_type&
|
|
||||||
lowest_layer()
|
|
||||||
{
|
|
||||||
return p_->lowest_layer();
|
|
||||||
}
|
|
||||||
|
|
||||||
lowest_layer_type const&
|
|
||||||
lowest_layer() const
|
|
||||||
{
|
|
||||||
return p_->lowest_layer();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
set_verify_mode(boost::asio::ssl::verify_mode v)
|
|
||||||
{
|
|
||||||
p_->next_layer().set_verify_mode(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
boost::system::error_code
|
|
||||||
set_verify_mode(boost::asio::ssl::verify_mode v,
|
|
||||||
boost::system::error_code& ec)
|
|
||||||
{
|
|
||||||
return p_->next_layer().set_verify_mode(v, ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
set_verify_depth(int depth)
|
|
||||||
{
|
|
||||||
p_->next_layer().set_verify_depth(depth);
|
|
||||||
}
|
|
||||||
|
|
||||||
boost::system::error_code
|
|
||||||
set_verify_depth(
|
|
||||||
int depth, boost::system::error_code& ec)
|
|
||||||
{
|
|
||||||
return p_->next_layer().set_verify_depth(depth, ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class VerifyCallback>
|
|
||||||
void
|
|
||||||
set_verify_callback(VerifyCallback callback)
|
|
||||||
{
|
|
||||||
p_->next_layer().set_verify_callback(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class VerifyCallback>
|
|
||||||
boost::system::error_code
|
|
||||||
set_verify_callback(VerifyCallback callback,
|
|
||||||
boost::system::error_code& ec)
|
|
||||||
{
|
|
||||||
return p_->next_layer().set_verify_callback(callback, ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
handshake(handshake_type type)
|
|
||||||
{
|
|
||||||
p_->next_layer().handshake(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
boost::system::error_code
|
|
||||||
handshake(handshake_type type,
|
|
||||||
boost::system::error_code& ec)
|
|
||||||
{
|
|
||||||
return p_->next_layer().handshake(type, ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class ConstBufferSequence>
|
|
||||||
void
|
|
||||||
handshake(
|
|
||||||
handshake_type type, ConstBufferSequence const& buffers)
|
|
||||||
{
|
|
||||||
p_->next_layer().handshake(type, buffers);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class ConstBufferSequence>
|
|
||||||
boost::system::error_code
|
|
||||||
handshake(handshake_type type,
|
|
||||||
ConstBufferSequence const& buffers,
|
|
||||||
boost::system::error_code& ec)
|
|
||||||
{
|
|
||||||
return p_->next_layer().handshake(type, buffers, ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class HandshakeHandler>
|
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(HandshakeHandler,
|
|
||||||
void(boost::system::error_code))
|
|
||||||
async_handshake(handshake_type type,
|
|
||||||
BOOST_ASIO_MOVE_ARG(HandshakeHandler) handler)
|
|
||||||
{
|
|
||||||
return p_->next_layer().async_handshake(type,
|
|
||||||
BOOST_ASIO_MOVE_CAST(HandshakeHandler)(handler));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class ConstBufferSequence, class BufferedHandshakeHandler>
|
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler,
|
|
||||||
void (boost::system::error_code, std::size_t))
|
|
||||||
async_handshake(handshake_type type, ConstBufferSequence const& buffers,
|
|
||||||
BOOST_ASIO_MOVE_ARG(BufferedHandshakeHandler) handler)
|
|
||||||
{
|
|
||||||
return p_->next_layer().async_handshake(type, buffers,
|
|
||||||
BOOST_ASIO_MOVE_CAST(BufferedHandshakeHandler)(handler));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
shutdown()
|
|
||||||
{
|
|
||||||
p_->next_layer().shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
boost::system::error_code
|
|
||||||
shutdown(boost::system::error_code& ec)
|
|
||||||
{
|
|
||||||
return p_->next_layer().shutdown(ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class ShutdownHandler>
|
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(ShutdownHandler,
|
|
||||||
void (boost::system::error_code))
|
|
||||||
async_shutdown(BOOST_ASIO_MOVE_ARG(ShutdownHandler) handler)
|
|
||||||
{
|
|
||||||
return p_->next_layer().async_shutdown(
|
|
||||||
BOOST_ASIO_MOVE_CAST(ShutdownHandler)(handler));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class ConstBufferSequence>
|
|
||||||
std::size_t
|
|
||||||
write_some(ConstBufferSequence const& buffers)
|
|
||||||
{
|
|
||||||
return p_->write_some(buffers);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class ConstBufferSequence>
|
|
||||||
std::size_t
|
|
||||||
write_some(ConstBufferSequence const& buffers,
|
|
||||||
boost::system::error_code& ec)
|
|
||||||
{
|
|
||||||
return p_->write_some(buffers, ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class ConstBufferSequence, class WriteHandler>
|
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler,
|
|
||||||
void (boost::system::error_code, std::size_t))
|
|
||||||
async_write_some(ConstBufferSequence const& buffers,
|
|
||||||
BOOST_ASIO_MOVE_ARG(WriteHandler) handler)
|
|
||||||
{
|
|
||||||
return p_->async_write_some(buffers,
|
|
||||||
BOOST_ASIO_MOVE_CAST(WriteHandler)(handler));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class MutableBufferSequence>
|
|
||||||
std::size_t
|
|
||||||
read_some(MutableBufferSequence const& buffers)
|
|
||||||
{
|
|
||||||
return p_->read_some(buffers);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class MutableBufferSequence>
|
|
||||||
std::size_t
|
|
||||||
read_some(MutableBufferSequence const& buffers,
|
|
||||||
boost::system::error_code& ec)
|
|
||||||
{
|
|
||||||
return p_->read_some(buffers, ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class MutableBufferSequence, class ReadHandler>
|
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,
|
|
||||||
void(boost::system::error_code, std::size_t))
|
|
||||||
async_read_some(MutableBufferSequence const& buffers,
|
|
||||||
BOOST_ASIO_MOVE_ARG(ReadHandler) handler)
|
|
||||||
{
|
|
||||||
return p_->async_read_some(buffers,
|
|
||||||
BOOST_ASIO_MOVE_CAST(ReadHandler)(handler));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class SyncStream>
|
|
||||||
friend
|
|
||||||
void
|
|
||||||
teardown(boost::beast::websocket::role_type,
|
|
||||||
ssl_stream<SyncStream>& stream,
|
|
||||||
boost::system::error_code& ec);
|
|
||||||
|
|
||||||
template<class AsyncStream, class TeardownHandler>
|
|
||||||
friend
|
|
||||||
void
|
|
||||||
async_teardown(boost::beast::websocket::role_type,
|
|
||||||
ssl_stream<AsyncStream>& stream, TeardownHandler&& handler);
|
|
||||||
};
|
|
||||||
|
|
||||||
// These hooks are used to inform boost::beast::websocket::stream on
|
|
||||||
// how to tear down the connection as part of the WebSocket
|
|
||||||
// protocol specifications
|
|
||||||
|
|
||||||
template<class SyncStream>
|
|
||||||
inline
|
|
||||||
void
|
|
||||||
teardown(
|
|
||||||
boost::beast::websocket::role_type role,
|
|
||||||
ssl_stream<SyncStream>& stream,
|
|
||||||
boost::system::error_code& ec)
|
|
||||||
{
|
|
||||||
// Just forward it to the wrapped stream
|
|
||||||
using boost::beast::websocket::teardown;
|
|
||||||
teardown(role, *stream.p_, ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class AsyncStream, class TeardownHandler>
|
|
||||||
inline
|
|
||||||
void
|
|
||||||
async_teardown(
|
|
||||||
boost::beast::websocket::role_type role,
|
|
||||||
ssl_stream<AsyncStream>& stream,
|
|
||||||
TeardownHandler&& handler)
|
|
||||||
{
|
|
||||||
// Just forward it to the wrapped stream
|
|
||||||
using boost::beast::websocket::async_teardown;
|
|
||||||
async_teardown(role,
|
|
||||||
*stream.p_, std::forward<TeardownHandler>(handler));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
64
include/boost/beast/experimental/core/detail/flat_stream.hpp
Normal file
64
include/boost/beast/experimental/core/detail/flat_stream.hpp
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
//
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BOOST_BEAST_CORE_DETAIL_FLAT_STREAM_HPP
|
||||||
|
#define BOOST_BEAST_CORE_DETAIL_FLAT_STREAM_HPP
|
||||||
|
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
|
namespace boost {
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
class flat_stream_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Largest buffer size we will flatten.
|
||||||
|
// 16KB is the upper limit on reasonably sized HTTP messages.
|
||||||
|
static std::size_t constexpr coalesce_limit = 16 * 1024;
|
||||||
|
|
||||||
|
// calculates the coalesce settings for a buffer sequence
|
||||||
|
template<class BufferSequence>
|
||||||
|
static
|
||||||
|
std::pair<std::size_t, bool>
|
||||||
|
coalesce(BufferSequence const& buffers, std::size_t limit)
|
||||||
|
{
|
||||||
|
std::pair<std::size_t, bool> result{0, false};
|
||||||
|
auto first = boost::asio::buffer_sequence_begin(buffers);
|
||||||
|
auto last = boost::asio::buffer_sequence_end(buffers);
|
||||||
|
if(first != last)
|
||||||
|
{
|
||||||
|
result.first = boost::asio::buffer_size(*first);
|
||||||
|
if(result.first < limit)
|
||||||
|
{
|
||||||
|
auto it = first;
|
||||||
|
auto prev = first;
|
||||||
|
while(++it != last)
|
||||||
|
{
|
||||||
|
auto const n =
|
||||||
|
boost::asio::buffer_size(*it);
|
||||||
|
if(result.first + n > limit)
|
||||||
|
break;
|
||||||
|
result.first += n;
|
||||||
|
prev = it;
|
||||||
|
}
|
||||||
|
result.second = prev != first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
|
} // boost
|
||||||
|
|
||||||
|
#endif
|
353
include/boost/beast/experimental/core/flat_stream.hpp
Normal file
353
include/boost/beast/experimental/core/flat_stream.hpp
Normal file
@@ -0,0 +1,353 @@
|
|||||||
|
//
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BOOST_BEAST_CORE_FLAT_STREAM_HPP
|
||||||
|
#define BOOST_BEAST_CORE_FLAT_STREAM_HPP
|
||||||
|
|
||||||
|
#include <boost/beast/core/error.hpp>
|
||||||
|
#include <boost/beast/core/type_traits.hpp>
|
||||||
|
#include <boost/beast/experimental/core/detail/flat_stream.hpp>
|
||||||
|
#include <boost/asio/async_result.hpp>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace boost {
|
||||||
|
namespace beast {
|
||||||
|
|
||||||
|
/** Stream wrapper to improve ssl::stream write performance.
|
||||||
|
|
||||||
|
This wrapper flattens writes for buffer sequences having length
|
||||||
|
greater than 1 and total size below a predefined amount, using
|
||||||
|
a dynamic memory allocation. It is primarily designed to overcome
|
||||||
|
a performance limitation of the current version of `boost::asio::ssl::stream`,
|
||||||
|
which does not use OpenSSL's scatter/gather interface for its
|
||||||
|
low-level read some and write some operations.
|
||||||
|
|
||||||
|
@par Example
|
||||||
|
|
||||||
|
To use the @ref flat_stream template with SSL streams, declare
|
||||||
|
a variable of the correct type. Parameters passed to the constructor
|
||||||
|
will be forwarded to the next layer's constructor:
|
||||||
|
|
||||||
|
@code
|
||||||
|
flat_stream<ssl::stream<ip::tcp::socket>> fs{ioc, ctx};
|
||||||
|
@endcode
|
||||||
|
Alternatively you can write
|
||||||
|
@code
|
||||||
|
ssl::stream<ip::tcp::socket> ss{ioc, ctx};
|
||||||
|
flat_stream<ssl::stream<ip::tcp::socket>&> fs{ss};
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
The resulting stream may be passed to any stream algorithms which
|
||||||
|
operate on synchronous or asynchronous read or write streams,
|
||||||
|
examples include:
|
||||||
|
|
||||||
|
@li `boost::asio::read`, `boost::asio::async_read`
|
||||||
|
|
||||||
|
@li `boost::asio::write`, `boost::asio::async_write`
|
||||||
|
|
||||||
|
@li `boost::asio::read_until`, `boost::asio::async_read_until`
|
||||||
|
|
||||||
|
The stream may also be used as a template parameter in other
|
||||||
|
stream wrappers, such as for websocket:
|
||||||
|
@code
|
||||||
|
websocket::stream<flat_stream<ssl::stream<ip::tcp::socket>>> ws{ioc, ctx};
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
@tparam NextLayer The type representing the next layer, to which
|
||||||
|
data will be read and written during operations. For synchronous
|
||||||
|
operations, the type must support the @b SyncStream concept. For
|
||||||
|
asynchronous operations, the type must support the @b AsyncStream
|
||||||
|
concept. This type will usually be some variation of
|
||||||
|
`boost::asio::ssl::stream`.
|
||||||
|
|
||||||
|
@par Concepts
|
||||||
|
@b AsyncStream
|
||||||
|
@b SyncStream
|
||||||
|
|
||||||
|
@see
|
||||||
|
@li https://github.com/boostorg/beast/issues/1108
|
||||||
|
@li https://github.com/boostorg/asio/issues/100
|
||||||
|
@li https://stackoverflow.com/questions/38198638/openssl-ssl-write-from-multiple-buffers-ssl-writev
|
||||||
|
@li https://stackoverflow.com/questions/50026167/performance-drop-on-port-from-beast-1-0-0-b66-to-boost-1-67-0-beast
|
||||||
|
*/
|
||||||
|
template<class NextLayer>
|
||||||
|
class flat_stream
|
||||||
|
#ifndef BOOST_BEAST_DOXYGEN
|
||||||
|
: private detail::flat_stream_base
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
// Largest buffer size we will flatten.
|
||||||
|
// 16KB is the upper limit on reasonably sized HTTP messages.
|
||||||
|
static std::size_t constexpr max_size = 16 * 1024;
|
||||||
|
|
||||||
|
template<class, class> class write_op;
|
||||||
|
|
||||||
|
NextLayer stream_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// The type of the next layer.
|
||||||
|
using next_layer_type =
|
||||||
|
typename std::remove_reference<NextLayer>::type;
|
||||||
|
|
||||||
|
/// The type of the lowest layer.
|
||||||
|
using lowest_layer_type = boost::beast::get_lowest_layer<next_layer_type>;
|
||||||
|
|
||||||
|
/// The type of the executor associated with the object.
|
||||||
|
using executor_type = typename next_layer_type::executor_type;
|
||||||
|
|
||||||
|
flat_stream(flat_stream&&) = default;
|
||||||
|
flat_stream(flat_stream const&) = default;
|
||||||
|
flat_stream& operator=(flat_stream&&) = default;
|
||||||
|
flat_stream& operator=(flat_stream const&) = default;
|
||||||
|
|
||||||
|
/** Destructor
|
||||||
|
|
||||||
|
The treatment of pending operations will be the same as that
|
||||||
|
of the next layer.
|
||||||
|
*/
|
||||||
|
~flat_stream() = default;
|
||||||
|
|
||||||
|
/** Constructor
|
||||||
|
|
||||||
|
Arguments, if any, are forwarded to the next layer's constructor.
|
||||||
|
*/
|
||||||
|
template<class... Args>
|
||||||
|
explicit
|
||||||
|
flat_stream(Args&&... args);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Get the executor associated with the object.
|
||||||
|
|
||||||
|
This function may be used to obtain the executor object that the
|
||||||
|
stream uses to dispatch handlers for asynchronous operations.
|
||||||
|
|
||||||
|
@return A copy of the executor that stream will use to dispatch handlers.
|
||||||
|
*/
|
||||||
|
executor_type
|
||||||
|
get_executor() noexcept
|
||||||
|
{
|
||||||
|
return stream_.get_executor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a reference to the next layer
|
||||||
|
|
||||||
|
This function returns a reference to the next layer
|
||||||
|
in a stack of stream layers.
|
||||||
|
|
||||||
|
@return A reference to the next layer in the stack of
|
||||||
|
stream layers.
|
||||||
|
*/
|
||||||
|
next_layer_type&
|
||||||
|
next_layer()
|
||||||
|
{
|
||||||
|
return stream_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a reference to the next layer
|
||||||
|
|
||||||
|
This function returns a reference to the next layer in a
|
||||||
|
stack of stream layers.
|
||||||
|
|
||||||
|
@return A reference to the next layer in the stack of
|
||||||
|
stream layers.
|
||||||
|
*/
|
||||||
|
next_layer_type const&
|
||||||
|
next_layer() const
|
||||||
|
{
|
||||||
|
return stream_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a reference to the lowest layer
|
||||||
|
|
||||||
|
This function returns a reference to the lowest layer
|
||||||
|
in a stack of stream layers.
|
||||||
|
|
||||||
|
@return A reference to the lowest layer in the stack of
|
||||||
|
stream layers.
|
||||||
|
*/
|
||||||
|
lowest_layer_type&
|
||||||
|
lowest_layer()
|
||||||
|
{
|
||||||
|
return stream_.lowest_layer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a reference to the lowest layer
|
||||||
|
|
||||||
|
This function returns a reference to the lowest layer
|
||||||
|
in a stack of stream layers.
|
||||||
|
|
||||||
|
@return A reference to the lowest layer in the stack of
|
||||||
|
stream layers. Ownership is not transferred to the caller.
|
||||||
|
*/
|
||||||
|
lowest_layer_type const&
|
||||||
|
lowest_layer() const
|
||||||
|
{
|
||||||
|
return stream_.lowest_layer();
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Read some data from the stream.
|
||||||
|
|
||||||
|
This function is used to read data from the stream. The function call will
|
||||||
|
block until one or more bytes of data has been read successfully, or until
|
||||||
|
an error occurs.
|
||||||
|
|
||||||
|
@param buffers The buffers into which the data will be read.
|
||||||
|
|
||||||
|
@returns The number of bytes read.
|
||||||
|
|
||||||
|
@throws boost::system::system_error Thrown on failure.
|
||||||
|
|
||||||
|
@note The `read_some` operation may not read all of the requested number of
|
||||||
|
bytes. Consider using the function `boost::asio::read` if you need to ensure
|
||||||
|
that the requested amount of data is read before the blocking operation
|
||||||
|
completes.
|
||||||
|
*/
|
||||||
|
template<class MutableBufferSequence>
|
||||||
|
std::size_t
|
||||||
|
read_some(MutableBufferSequence const& buffers);
|
||||||
|
|
||||||
|
/** Read some data from the stream.
|
||||||
|
|
||||||
|
This function is used to read data from the stream. The function call will
|
||||||
|
block until one or more bytes of data has been read successfully, or until
|
||||||
|
an error occurs.
|
||||||
|
|
||||||
|
@param buffers The buffers into which the data will be read.
|
||||||
|
|
||||||
|
@param ec Set to indicate what error occurred, if any.
|
||||||
|
|
||||||
|
@returns The number of bytes read.
|
||||||
|
|
||||||
|
@note The `read_some` operation may not read all of the requested number of
|
||||||
|
bytes. Consider using the function `boost::asio::read` if you need to ensure
|
||||||
|
that the requested amount of data is read before the blocking operation
|
||||||
|
completes.
|
||||||
|
*/
|
||||||
|
template<class MutableBufferSequence>
|
||||||
|
std::size_t
|
||||||
|
read_some(
|
||||||
|
MutableBufferSequence const& buffers,
|
||||||
|
error_code& ec);
|
||||||
|
|
||||||
|
/** Start an asynchronous read.
|
||||||
|
|
||||||
|
This function is used to asynchronously read one or more bytes of data from
|
||||||
|
the stream. The function call always returns immediately.
|
||||||
|
|
||||||
|
@param buffers The buffers into which the data will be read. 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 read operation completes.
|
||||||
|
Copies will be made of the handler as required. The equivalent function
|
||||||
|
signature of the handler must be:
|
||||||
|
@code void handler(
|
||||||
|
const boost::system::error_code& error, // Result of operation.
|
||||||
|
std::size_t bytes_transferred // Number of bytes read.
|
||||||
|
); @endcode
|
||||||
|
|
||||||
|
@note The `read_some` operation may not read all of the requested number of
|
||||||
|
bytes. Consider using the function `boost::asio::async_read` if you need
|
||||||
|
to ensure that the requested amount of data is read before the asynchronous
|
||||||
|
operation completes.
|
||||||
|
*/
|
||||||
|
template<
|
||||||
|
class MutableBufferSequence,
|
||||||
|
class ReadHandler>
|
||||||
|
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||||
|
ReadHandler, void(error_code, std::size_t))
|
||||||
|
async_read_some(
|
||||||
|
MutableBufferSequence const& buffers,
|
||||||
|
ReadHandler&& handler);
|
||||||
|
|
||||||
|
/** Write some data to the stream.
|
||||||
|
|
||||||
|
This function is used to write data on the stream. The function call will
|
||||||
|
block until one or more bytes of data has been written successfully, or
|
||||||
|
until an error occurs.
|
||||||
|
|
||||||
|
@param buffers The data to be written.
|
||||||
|
|
||||||
|
@returns The number of bytes written.
|
||||||
|
|
||||||
|
@throws boost::system::system_error Thrown on failure.
|
||||||
|
|
||||||
|
@note The `write_some` operation may not transmit all of the data to the
|
||||||
|
peer. Consider using the function `boost::asio::write` if you need to
|
||||||
|
ensure that all data is written before the blocking operation completes.
|
||||||
|
*/
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
std::size_t
|
||||||
|
write_some(ConstBufferSequence const& buffers);
|
||||||
|
|
||||||
|
/** Write some data to the stream.
|
||||||
|
|
||||||
|
This function is used to write data on the stream. The function call will
|
||||||
|
block until one or more bytes of data has been written successfully, or
|
||||||
|
until an error occurs.
|
||||||
|
|
||||||
|
@param buffers The data to be written.
|
||||||
|
|
||||||
|
@param ec Set to indicate what error occurred, if any.
|
||||||
|
|
||||||
|
@returns The number of bytes written.
|
||||||
|
|
||||||
|
@note The `write_some` operation may not transmit all of the data to the
|
||||||
|
peer. Consider using the function `boost::asio::write` if you need to
|
||||||
|
ensure that all data is written before the blocking operation completes.
|
||||||
|
*/
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
std::size_t
|
||||||
|
write_some(
|
||||||
|
ConstBufferSequence const& buffers,
|
||||||
|
error_code& ec);
|
||||||
|
|
||||||
|
/** Start an asynchronous write.
|
||||||
|
|
||||||
|
This function is used to asynchronously write one or more bytes of data to
|
||||||
|
the stream. The function call always returns immediately.
|
||||||
|
|
||||||
|
@param buffers The data to be written to the stream. 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 write operation completes.
|
||||||
|
Copies will be made of the handler as required. The equivalent function
|
||||||
|
signature of the handler must be:
|
||||||
|
@code void handler(
|
||||||
|
const boost::system::error_code& error, // Result of operation.
|
||||||
|
std::size_t bytes_transferred // Number of bytes written.
|
||||||
|
); @endcode
|
||||||
|
|
||||||
|
@note The `async_write_some` operation may not transmit all of the data to
|
||||||
|
the peer. Consider using the function `boost::asio::async_write` if you need
|
||||||
|
to ensure that all data is written before the asynchronous operation completes.
|
||||||
|
*/
|
||||||
|
template<
|
||||||
|
class ConstBufferSequence,
|
||||||
|
class WriteHandler>
|
||||||
|
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||||
|
WriteHandler, void(error_code, std::size_t))
|
||||||
|
async_write_some(
|
||||||
|
ConstBufferSequence const& buffers,
|
||||||
|
WriteHandler&& handler);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // beast
|
||||||
|
} // boost
|
||||||
|
|
||||||
|
#include <boost/beast/experimental/core/impl/flat_stream.ipp>
|
||||||
|
|
||||||
|
#endif
|
304
include/boost/beast/experimental/core/impl/flat_stream.ipp
Normal file
304
include/boost/beast/experimental/core/impl/flat_stream.ipp
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
//
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BOOST_BEAST_CORE_IMPL_FLAT_STREAM_IPP
|
||||||
|
#define BOOST_BEAST_CORE_IMPL_FLAT_STREAM_IPP
|
||||||
|
|
||||||
|
#include <boost/beast/core/buffers_prefix.hpp>
|
||||||
|
#include <boost/beast/websocket/teardown.hpp>
|
||||||
|
#include <boost/asio/associated_allocator.hpp>
|
||||||
|
#include <boost/asio/associated_executor.hpp>
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <boost/asio/coroutine.hpp>
|
||||||
|
#include <boost/asio/handler_continuation_hook.hpp>
|
||||||
|
#include <boost/asio/handler_invoke_hook.hpp>
|
||||||
|
|
||||||
|
namespace boost {
|
||||||
|
namespace beast {
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class ConstBufferSequence, class Handler>
|
||||||
|
class flat_stream<NextLayer>::write_op
|
||||||
|
: public boost::asio::coroutine
|
||||||
|
{
|
||||||
|
using alloc_type = typename
|
||||||
|
boost::asio::associated_allocator_t<Handler>::template
|
||||||
|
rebind<char>::other;
|
||||||
|
|
||||||
|
struct deleter
|
||||||
|
{
|
||||||
|
alloc_type alloc;
|
||||||
|
std::size_t size = 0;
|
||||||
|
|
||||||
|
explicit
|
||||||
|
deleter(alloc_type const& alloc_)
|
||||||
|
: alloc(alloc_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()(char* p)
|
||||||
|
{
|
||||||
|
alloc.deallocate(p, size);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
flat_stream<NextLayer>& s_;
|
||||||
|
ConstBufferSequence b_;
|
||||||
|
std::unique_ptr<char, deleter> p_;
|
||||||
|
Handler h_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<class DeducedHandler>
|
||||||
|
write_op(
|
||||||
|
flat_stream<NextLayer>& s,
|
||||||
|
ConstBufferSequence const& b,
|
||||||
|
DeducedHandler&& h)
|
||||||
|
: s_(s)
|
||||||
|
, b_(b)
|
||||||
|
, p_(nullptr, deleter{
|
||||||
|
(boost::asio::get_associated_allocator)(h)})
|
||||||
|
, h_(std::forward<DeducedHandler>(h))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
using allocator_type =
|
||||||
|
boost::asio::associated_allocator_t<Handler>;
|
||||||
|
|
||||||
|
allocator_type
|
||||||
|
get_allocator() const noexcept
|
||||||
|
{
|
||||||
|
return (boost::asio::get_associated_allocator)(h_);
|
||||||
|
}
|
||||||
|
|
||||||
|
using executor_type = boost::asio::associated_executor_t<
|
||||||
|
Handler, decltype(std::declval<NextLayer&>().get_executor())>;
|
||||||
|
|
||||||
|
executor_type
|
||||||
|
get_executor() const noexcept
|
||||||
|
{
|
||||||
|
return (boost::asio::get_associated_executor)(
|
||||||
|
h_, s_.get_executor());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()(
|
||||||
|
boost::system::error_code ec,
|
||||||
|
std::size_t bytes_transferred);
|
||||||
|
|
||||||
|
friend
|
||||||
|
bool asio_handler_is_continuation(write_op* op)
|
||||||
|
{
|
||||||
|
using boost::asio::asio_handler_is_continuation;
|
||||||
|
return asio_handler_is_continuation(
|
||||||
|
std::addressof(op->h_));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Function>
|
||||||
|
friend
|
||||||
|
void asio_handler_invoke(Function&& f, write_op* op)
|
||||||
|
{
|
||||||
|
using boost::asio::asio_handler_invoke;
|
||||||
|
asio_handler_invoke(f, std::addressof(op->h_));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class ConstBufferSequence, class Handler>
|
||||||
|
void
|
||||||
|
flat_stream<NextLayer>::
|
||||||
|
write_op<ConstBufferSequence, Handler>::
|
||||||
|
operator()(
|
||||||
|
error_code ec,
|
||||||
|
std::size_t bytes_transferred)
|
||||||
|
{
|
||||||
|
BOOST_ASIO_CORO_REENTER(*this)
|
||||||
|
{
|
||||||
|
BOOST_ASIO_CORO_YIELD
|
||||||
|
{
|
||||||
|
auto const result = coalesce(b_, coalesce_limit);
|
||||||
|
if(result.second)
|
||||||
|
{
|
||||||
|
p_.get_deleter().size = result.first;
|
||||||
|
p_.reset(p_.get_deleter().alloc.allocate(
|
||||||
|
p_.get_deleter().size));
|
||||||
|
boost::asio::buffer_copy(
|
||||||
|
boost::asio::buffer(
|
||||||
|
p_.get(), p_.get_deleter().size),
|
||||||
|
b_, result.first);
|
||||||
|
s_.stream_.async_write_some(
|
||||||
|
boost::asio::buffer(
|
||||||
|
p_.get(), p_.get_deleter().size),
|
||||||
|
std::move(*this));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s_.stream_.async_write_some(
|
||||||
|
boost::beast::buffers_prefix(result.first, b_),
|
||||||
|
std::move(*this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p_.reset();
|
||||||
|
h_(ec, bytes_transferred);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class... Args>
|
||||||
|
flat_stream<NextLayer>::
|
||||||
|
flat_stream(Args&&... args)
|
||||||
|
: stream_(std::forward<Args>(args)...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class MutableBufferSequence>
|
||||||
|
std::size_t
|
||||||
|
flat_stream<NextLayer>::
|
||||||
|
read_some(MutableBufferSequence const& buffers)
|
||||||
|
{
|
||||||
|
static_assert(boost::beast::is_sync_read_stream<next_layer_type>::value,
|
||||||
|
"SyncReadStream requirements not met");
|
||||||
|
static_assert(boost::asio::is_mutable_buffer_sequence<
|
||||||
|
MutableBufferSequence>::value,
|
||||||
|
"MutableBufferSequence requirements not met");
|
||||||
|
error_code ec;
|
||||||
|
auto n = read_some(buffers, ec);
|
||||||
|
if(ec)
|
||||||
|
BOOST_THROW_EXCEPTION(boost::system::system_error{ec});
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class MutableBufferSequence>
|
||||||
|
std::size_t
|
||||||
|
flat_stream<NextLayer>::
|
||||||
|
read_some(MutableBufferSequence const& buffers, error_code& ec)
|
||||||
|
{
|
||||||
|
return stream_.read_some(buffers, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<
|
||||||
|
class MutableBufferSequence,
|
||||||
|
class ReadHandler>
|
||||||
|
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||||
|
ReadHandler, void(error_code, std::size_t))
|
||||||
|
flat_stream<NextLayer>::
|
||||||
|
async_read_some(
|
||||||
|
MutableBufferSequence const& buffers,
|
||||||
|
ReadHandler&& handler)
|
||||||
|
{
|
||||||
|
static_assert(boost::beast::is_async_read_stream<next_layer_type>::value,
|
||||||
|
"AsyncReadStream requirements not met");
|
||||||
|
static_assert(boost::asio::is_mutable_buffer_sequence<
|
||||||
|
MutableBufferSequence >::value,
|
||||||
|
"MutableBufferSequence requirements not met");
|
||||||
|
return stream_.async_read_some(
|
||||||
|
buffers, std::forward<ReadHandler>(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
std::size_t
|
||||||
|
flat_stream<NextLayer>::
|
||||||
|
write_some(ConstBufferSequence const& buffers)
|
||||||
|
{
|
||||||
|
static_assert(boost::beast::is_sync_write_stream<next_layer_type>::value,
|
||||||
|
"SyncWriteStream requirements not met");
|
||||||
|
static_assert(boost::asio::is_const_buffer_sequence<
|
||||||
|
ConstBufferSequence>::value,
|
||||||
|
"ConstBufferSequence requirements not met");
|
||||||
|
auto const result = coalesce(buffers, coalesce_limit);
|
||||||
|
if(result.second)
|
||||||
|
{
|
||||||
|
std::unique_ptr<char[]> p{new char[result.first]};
|
||||||
|
auto const b = boost::asio::buffer(p.get(), result.first);
|
||||||
|
boost::asio::buffer_copy(b, buffers);
|
||||||
|
return stream_.write_some(b);
|
||||||
|
}
|
||||||
|
return stream_.write_some(
|
||||||
|
boost::beast::buffers_prefix(result.first, buffers));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
std::size_t
|
||||||
|
flat_stream<NextLayer>::
|
||||||
|
write_some(ConstBufferSequence const& buffers, error_code& ec)
|
||||||
|
{
|
||||||
|
static_assert(boost::beast::is_sync_write_stream<next_layer_type>::value,
|
||||||
|
"SyncWriteStream requirements not met");
|
||||||
|
static_assert(boost::asio::is_const_buffer_sequence<
|
||||||
|
ConstBufferSequence>::value,
|
||||||
|
"ConstBufferSequence requirements not met");
|
||||||
|
auto const result = coalesce(buffers, coalesce_limit);
|
||||||
|
if(result.second)
|
||||||
|
{
|
||||||
|
std::unique_ptr<char[]> p{new char[result.first]};
|
||||||
|
auto const b = boost::asio::buffer(p.get(), result.first);
|
||||||
|
boost::asio::buffer_copy(b, buffers);
|
||||||
|
return stream_.write_some(b, ec);
|
||||||
|
}
|
||||||
|
return stream_.write_some(
|
||||||
|
boost::beast::buffers_prefix(result.first, buffers), ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<
|
||||||
|
class ConstBufferSequence,
|
||||||
|
class WriteHandler>
|
||||||
|
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||||
|
WriteHandler, void(error_code, std::size_t))
|
||||||
|
flat_stream<NextLayer>::
|
||||||
|
async_write_some(
|
||||||
|
ConstBufferSequence const& buffers,
|
||||||
|
WriteHandler&& handler)
|
||||||
|
{
|
||||||
|
static_assert(boost::beast::is_async_write_stream<next_layer_type>::value,
|
||||||
|
"AsyncWriteStream requirements not met");
|
||||||
|
static_assert(boost::asio::is_const_buffer_sequence<
|
||||||
|
ConstBufferSequence>::value,
|
||||||
|
"ConstBufferSequence requirements not met");
|
||||||
|
BOOST_BEAST_HANDLER_INIT(
|
||||||
|
WriteHandler, void(error_code, std::size_t));
|
||||||
|
write_op<ConstBufferSequence, BOOST_ASIO_HANDLER_TYPE(
|
||||||
|
WriteHandler, void(error_code, std::size_t))>{
|
||||||
|
*this, buffers, std::move(init.completion_handler)}({}, 0);
|
||||||
|
return init.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
void
|
||||||
|
teardown(
|
||||||
|
boost::beast::websocket::role_type role,
|
||||||
|
flat_stream<NextLayer>& s,
|
||||||
|
error_code& ec)
|
||||||
|
{
|
||||||
|
using boost::beast::websocket::teardown;
|
||||||
|
teardown(role, s.next_layer(), ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer, class TeardownHandler>
|
||||||
|
void
|
||||||
|
async_teardown(
|
||||||
|
boost::beast::websocket::role_type role,
|
||||||
|
flat_stream<NextLayer>& s,
|
||||||
|
TeardownHandler&& handler)
|
||||||
|
{
|
||||||
|
using boost::beast::websocket::async_teardown;
|
||||||
|
async_teardown(role, s.next_layer(), std::move(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // beast
|
||||||
|
} // boost
|
||||||
|
|
||||||
|
#endif
|
@@ -27,6 +27,7 @@ add_executable (tests-beast
|
|||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory (core)
|
add_subdirectory (core)
|
||||||
|
add_subdirectory (experimental)
|
||||||
add_subdirectory (http)
|
add_subdirectory (http)
|
||||||
add_subdirectory (websocket)
|
add_subdirectory (websocket)
|
||||||
add_subdirectory (zlib)
|
add_subdirectory (zlib)
|
||||||
|
@@ -14,6 +14,7 @@ alias run-tests :
|
|||||||
[ compile websocket.cpp ]
|
[ compile websocket.cpp ]
|
||||||
[ compile zlib.cpp ]
|
[ compile zlib.cpp ]
|
||||||
core//run-tests
|
core//run-tests
|
||||||
|
experimental//run-tests
|
||||||
http//run-tests
|
http//run-tests
|
||||||
websocket//run-tests
|
websocket//run-tests
|
||||||
zlib//run-tests
|
zlib//run-tests
|
||||||
@@ -21,6 +22,7 @@ alias run-tests :
|
|||||||
|
|
||||||
alias fat-tests :
|
alias fat-tests :
|
||||||
core//fat-tests
|
core//fat-tests
|
||||||
|
experimental//fat-tests
|
||||||
http//fat-tests
|
http//fat-tests
|
||||||
websocket//fat-tests
|
websocket//fat-tests
|
||||||
zlib//fat-tests
|
zlib//fat-tests
|
||||||
|
23
test/beast/experimental/CMakeLists.txt
Normal file
23
test/beast/experimental/CMakeLists.txt
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#
|
||||||
|
# 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
|
||||||
|
#
|
||||||
|
|
||||||
|
GroupSources(test/extras/include/boost/beast extras)
|
||||||
|
GroupSources(subtree/unit_test/include/boost/beast extras)
|
||||||
|
GroupSources(include/boost/beast beast)
|
||||||
|
GroupSources(test/beast/experimental "/")
|
||||||
|
|
||||||
|
add_executable (tests-beast-experimental
|
||||||
|
${BOOST_BEAST_FILES}
|
||||||
|
${EXTRAS_FILES}
|
||||||
|
${TEST_MAIN}
|
||||||
|
Jamfile
|
||||||
|
flat_stream.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set_property(TARGET tests-beast-experimental PROPERTY FOLDER "tests")
|
25
test/beast/experimental/Jamfile
Normal file
25
test/beast/experimental/Jamfile
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#
|
||||||
|
# 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
|
||||||
|
#
|
||||||
|
|
||||||
|
local SOURCES =
|
||||||
|
flat_stream.cpp
|
||||||
|
;
|
||||||
|
|
||||||
|
local RUN_TESTS ;
|
||||||
|
|
||||||
|
for local f in $(SOURCES)
|
||||||
|
{
|
||||||
|
RUN_TESTS += [ run $(f) $(TEST_MAIN) ] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
alias run-tests : $(RUN_TESTS) ;
|
||||||
|
|
||||||
|
exe fat-tests : $(TEST_MAIN) $(SOURCES) ;
|
||||||
|
|
||||||
|
explicit fat-tests ;
|
@@ -8,7 +8,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
// Test that header file is self-contained.
|
// Test that header file is self-contained.
|
||||||
#include <example/common/flat_stream.hpp>
|
#include <boost/beast/experimental/core/flat_stream.hpp>
|
||||||
|
|
||||||
#include <boost/beast/test/websocket.hpp>
|
#include <boost/beast/test/websocket.hpp>
|
||||||
#include <boost/beast/test/yield_to.hpp>
|
#include <boost/beast/test/yield_to.hpp>
|
||||||
@@ -39,7 +39,7 @@ public:
|
|||||||
for(auto const n : v0)
|
for(auto const n : v0)
|
||||||
v.emplace_back("", n);
|
v.emplace_back("", n);
|
||||||
auto const result =
|
auto const result =
|
||||||
::detail::flat_stream_base::coalesce(v, limit);
|
boost::beast::detail::flat_stream_base::coalesce(v, limit);
|
||||||
BEAST_EXPECT(result.first == count);
|
BEAST_EXPECT(result.first == count);
|
||||||
BEAST_EXPECT(result.second == copy);
|
BEAST_EXPECT(result.second == copy);
|
||||||
return result;
|
return result;
|
@@ -20,7 +20,6 @@ add_executable (tests-example-common
|
|||||||
${TEST_MAIN}
|
${TEST_MAIN}
|
||||||
Jamfile
|
Jamfile
|
||||||
detect_ssl.cpp
|
detect_ssl.cpp
|
||||||
flat_stream.cpp
|
|
||||||
root_certificates.cpp
|
root_certificates.cpp
|
||||||
server_certificate.cpp
|
server_certificate.cpp
|
||||||
session_alloc.cpp
|
session_alloc.cpp
|
||||||
|
@@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
local SOURCES =
|
local SOURCES =
|
||||||
detect_ssl.cpp
|
detect_ssl.cpp
|
||||||
flat_stream.cpp
|
|
||||||
root_certificates.cpp
|
root_certificates.cpp
|
||||||
server_certificate.cpp
|
server_certificate.cpp
|
||||||
session_alloc.cpp
|
session_alloc.cpp
|
||||||
|
Reference in New Issue
Block a user