mirror of
https://github.com/boostorg/beast.git
synced 2025-07-29 12:27:44 +02:00
Add test::stream to experimental
This commit is contained in:
@ -4,6 +4,7 @@ Version 170:
|
||||
* Add ssl_stream to experimental
|
||||
* Add test::error to experimental
|
||||
* Add test::fail_count to experimental
|
||||
* Add test::stream to experimental
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
@ -303,6 +303,7 @@
|
||||
<member><link linkend="beast.ref.boost__beast__flat_stream">flat_stream</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__ssl_stream">ssl_stream</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__test__fail_count">test::fail_count</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__test__stream">test::stream</link></member>
|
||||
</simplelist>
|
||||
</entry>
|
||||
<entry valign="top">
|
||||
|
@ -7,375 +7,147 @@
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_TEST_STREAM_HPP
|
||||
#define BOOST_BEAST_TEST_STREAM_HPP
|
||||
#ifndef BOOST_BEAST_TEST_IMPL_STREAM_IPP
|
||||
#define BOOST_BEAST_TEST_IMPL_STREAM_IPP
|
||||
|
||||
#include <boost/beast/core/bind_handler.hpp>
|
||||
#include <boost/beast/core/buffers_prefix.hpp>
|
||||
#include <boost/beast/core/flat_buffer.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/beast/websocket/teardown.hpp>
|
||||
#include <boost/beast/experimental/test/fail_count.hpp>
|
||||
#include <boost/asio/async_result.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/asio/executor_work_guard.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <condition_variable>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace test {
|
||||
|
||||
/** A bidirectional in-memory communication channel
|
||||
|
||||
An instance of this class provides a client and server
|
||||
endpoint that are automatically connected to each other
|
||||
similarly to a connected socket.
|
||||
|
||||
Test pipes are used to facilitate writing unit tests
|
||||
where the behavior of the transport is tightly controlled
|
||||
to help illuminate all code paths (for code coverage)
|
||||
*/
|
||||
class stream
|
||||
inline
|
||||
stream::
|
||||
~stream()
|
||||
{
|
||||
struct read_op
|
||||
{
|
||||
virtual ~read_op() = default;
|
||||
virtual void operator()() = 0;
|
||||
};
|
||||
|
||||
template<class Handler, class Buffers>
|
||||
class read_op_impl;
|
||||
|
||||
enum class status
|
||||
std::unique_lock<std::mutex> lock{in_->m};
|
||||
in_->op.reset();
|
||||
}
|
||||
auto out = out_.lock();
|
||||
if(out)
|
||||
{
|
||||
ok,
|
||||
eof,
|
||||
reset
|
||||
};
|
||||
|
||||
struct state
|
||||
{
|
||||
friend class stream;
|
||||
|
||||
std::mutex m;
|
||||
flat_buffer b;
|
||||
std::condition_variable cv;
|
||||
std::unique_ptr<read_op> op;
|
||||
boost::asio::io_context& ioc;
|
||||
status code = status::ok;
|
||||
fail_count* fc = nullptr;
|
||||
std::size_t nread = 0;
|
||||
std::size_t nwrite = 0;
|
||||
std::size_t read_max =
|
||||
(std::numeric_limits<std::size_t>::max)();
|
||||
std::size_t write_max =
|
||||
(std::numeric_limits<std::size_t>::max)();
|
||||
|
||||
~state()
|
||||
std::unique_lock<std::mutex> lock{out->m};
|
||||
if(out->code == status::ok)
|
||||
{
|
||||
BOOST_ASSERT(! op);
|
||||
}
|
||||
|
||||
explicit
|
||||
state(
|
||||
boost::asio::io_context& ioc_,
|
||||
fail_count* fc_)
|
||||
: ioc(ioc_)
|
||||
, fc(fc_)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
on_write()
|
||||
{
|
||||
if(op)
|
||||
{
|
||||
std::unique_ptr<read_op> op_ = std::move(op);
|
||||
op_->operator()();
|
||||
}
|
||||
else
|
||||
{
|
||||
cv.notify_all();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<state> in_;
|
||||
std::weak_ptr<state> out_;
|
||||
|
||||
public:
|
||||
using buffer_type = flat_buffer;
|
||||
|
||||
/// The type of the lowest layer.
|
||||
using lowest_layer_type = stream;
|
||||
|
||||
/// Destructor
|
||||
~stream()
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lock{in_->m};
|
||||
in_->op.reset();
|
||||
}
|
||||
auto out = out_.lock();
|
||||
if(out)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock{out->m};
|
||||
if(out->code == status::ok)
|
||||
{
|
||||
out->code = status::reset;
|
||||
out->on_write();
|
||||
}
|
||||
out->code = status::reset;
|
||||
out->on_write();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructor
|
||||
stream(stream&& other)
|
||||
{
|
||||
auto in = std::make_shared<state>(
|
||||
other.in_->ioc, other.in_->fc);
|
||||
in_ = std::move(other.in_);
|
||||
out_ = std::move(other.out_);
|
||||
other.in_ = in;
|
||||
}
|
||||
inline
|
||||
stream::
|
||||
stream(stream&& other)
|
||||
{
|
||||
auto in = std::make_shared<state>(
|
||||
other.in_->ioc, other.in_->fc);
|
||||
in_ = std::move(other.in_);
|
||||
out_ = std::move(other.out_);
|
||||
other.in_ = in;
|
||||
}
|
||||
|
||||
/// Assignment
|
||||
stream&
|
||||
operator=(stream&& other)
|
||||
{
|
||||
auto in = std::make_shared<state>(
|
||||
other.in_->ioc, other.in_->fc);
|
||||
in_ = std::move(other.in_);
|
||||
out_ = std::move(other.out_);
|
||||
other.in_ = in;
|
||||
return *this;
|
||||
}
|
||||
inline
|
||||
stream&
|
||||
stream::
|
||||
operator=(stream&& other)
|
||||
{
|
||||
auto in = std::make_shared<state>(
|
||||
other.in_->ioc, other.in_->fc);
|
||||
in_ = std::move(other.in_);
|
||||
out_ = std::move(other.out_);
|
||||
other.in_ = in;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Constructor
|
||||
explicit
|
||||
stream(boost::asio::io_context& ioc)
|
||||
: in_(std::make_shared<state>(ioc, nullptr))
|
||||
{
|
||||
}
|
||||
inline
|
||||
stream::
|
||||
stream(boost::asio::io_context& ioc)
|
||||
: in_(std::make_shared<state>(ioc, nullptr))
|
||||
{
|
||||
}
|
||||
|
||||
/// Constructor
|
||||
stream(
|
||||
boost::asio::io_context& ioc,
|
||||
fail_count& fc)
|
||||
: in_(std::make_shared<state>(ioc, &fc))
|
||||
{
|
||||
}
|
||||
inline
|
||||
stream::
|
||||
stream(
|
||||
boost::asio::io_context& ioc,
|
||||
fail_count& fc)
|
||||
: in_(std::make_shared<state>(ioc, &fc))
|
||||
{
|
||||
}
|
||||
|
||||
/// Constructor
|
||||
stream(
|
||||
boost::asio::io_context& ioc,
|
||||
string_view s)
|
||||
: in_(std::make_shared<state>(ioc, nullptr))
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
in_->b.commit(buffer_copy(
|
||||
in_->b.prepare(s.size()),
|
||||
buffer(s.data(), s.size())));
|
||||
}
|
||||
inline
|
||||
stream::
|
||||
stream(
|
||||
boost::asio::io_context& ioc,
|
||||
string_view s)
|
||||
: in_(std::make_shared<state>(ioc, nullptr))
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
in_->b.commit(buffer_copy(
|
||||
in_->b.prepare(s.size()),
|
||||
buffer(s.data(), s.size())));
|
||||
}
|
||||
|
||||
/// Constructor
|
||||
stream(
|
||||
boost::asio::io_context& ioc,
|
||||
fail_count& fc,
|
||||
string_view s)
|
||||
: in_(std::make_shared<state>(ioc, &fc))
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
in_->b.commit(buffer_copy(
|
||||
in_->b.prepare(s.size()),
|
||||
buffer(s.data(), s.size())));
|
||||
}
|
||||
inline
|
||||
stream::
|
||||
stream(
|
||||
boost::asio::io_context& ioc,
|
||||
fail_count& fc,
|
||||
string_view s)
|
||||
: in_(std::make_shared<state>(ioc, &fc))
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
in_->b.commit(buffer_copy(
|
||||
in_->b.prepare(s.size()),
|
||||
buffer(s.data(), s.size())));
|
||||
}
|
||||
|
||||
/// Establish a connection
|
||||
void
|
||||
connect(stream& remote)
|
||||
{
|
||||
BOOST_ASSERT(! out_.lock());
|
||||
BOOST_ASSERT(! remote.out_.lock());
|
||||
out_ = remote.in_;
|
||||
remote.out_ = in_;
|
||||
}
|
||||
inline
|
||||
void
|
||||
stream::
|
||||
connect(stream& remote)
|
||||
{
|
||||
BOOST_ASSERT(! out_.lock());
|
||||
BOOST_ASSERT(! remote.out_.lock());
|
||||
out_ = remote.in_;
|
||||
remote.out_ = in_;
|
||||
}
|
||||
inline
|
||||
string_view
|
||||
stream::
|
||||
str() const
|
||||
{
|
||||
auto const bs = in_->b.data();
|
||||
if(boost::asio::buffer_size(bs) == 0)
|
||||
return {};
|
||||
auto const b = buffers_front(bs);
|
||||
return {reinterpret_cast<char const*>(b.data()), b.size()};
|
||||
}
|
||||
|
||||
/// The type of the executor associated with the object.
|
||||
using executor_type =
|
||||
boost::asio::io_context::executor_type;
|
||||
inline
|
||||
void
|
||||
stream::
|
||||
append(string_view s)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
std::lock_guard<std::mutex> lock{in_->m};
|
||||
in_->b.commit(buffer_copy(
|
||||
in_->b.prepare(s.size()),
|
||||
buffer(s.data(), s.size())));
|
||||
}
|
||||
|
||||
/// Return the executor associated with the object.
|
||||
boost::asio::io_context::executor_type
|
||||
get_executor() noexcept
|
||||
{
|
||||
return in_->ioc.get_executor();
|
||||
};
|
||||
|
||||
/** 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 *this;
|
||||
}
|
||||
|
||||
/** 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 *this;
|
||||
}
|
||||
|
||||
/// Set the maximum number of bytes returned by read_some
|
||||
void
|
||||
read_size(std::size_t n)
|
||||
{
|
||||
in_->read_max = n;
|
||||
}
|
||||
|
||||
/// Set the maximum number of bytes returned by write_some
|
||||
void
|
||||
write_size(std::size_t n)
|
||||
{
|
||||
in_->write_max = n;
|
||||
}
|
||||
|
||||
/// Direct input buffer access
|
||||
buffer_type&
|
||||
buffer()
|
||||
{
|
||||
return in_->b;
|
||||
}
|
||||
|
||||
/// Returns a string view representing the pending input data
|
||||
string_view
|
||||
str() const
|
||||
{
|
||||
auto const bs = in_->b.data();
|
||||
if(boost::asio::buffer_size(bs) == 0)
|
||||
return {};
|
||||
auto const b = buffers_front(bs);
|
||||
return {reinterpret_cast<char const*>(b.data()), b.size()};
|
||||
}
|
||||
|
||||
/// Appends a string to the pending input data
|
||||
void
|
||||
append(string_view s)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
std::lock_guard<std::mutex> lock{in_->m};
|
||||
in_->b.commit(buffer_copy(
|
||||
in_->b.prepare(s.size()),
|
||||
buffer(s.data(), s.size())));
|
||||
}
|
||||
|
||||
/// Clear the pending input area
|
||||
void
|
||||
clear()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock{in_->m};
|
||||
in_->b.consume(in_->b.size());
|
||||
}
|
||||
|
||||
/// Return the number of reads
|
||||
std::size_t
|
||||
nread() const
|
||||
{
|
||||
return in_->nread;
|
||||
}
|
||||
|
||||
/// Return the number of writes
|
||||
std::size_t
|
||||
nwrite() const
|
||||
{
|
||||
return in_->nwrite;
|
||||
}
|
||||
|
||||
/** Close the stream.
|
||||
|
||||
The other end of the connection will see
|
||||
`error::eof` after reading all the remaining data.
|
||||
*/
|
||||
void
|
||||
close();
|
||||
|
||||
/** Close the other end of the stream.
|
||||
|
||||
This end of the connection will see
|
||||
`error::eof` after reading all the remaining data.
|
||||
*/
|
||||
void
|
||||
close_remote();
|
||||
|
||||
template<class MutableBufferSequence>
|
||||
std::size_t
|
||||
read_some(MutableBufferSequence const& buffers);
|
||||
|
||||
template<class MutableBufferSequence>
|
||||
std::size_t
|
||||
read_some(MutableBufferSequence const& buffers,
|
||||
error_code& ec);
|
||||
|
||||
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);
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
write_some(ConstBufferSequence const& buffers);
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
write_some(
|
||||
ConstBufferSequence const& buffers, error_code&);
|
||||
|
||||
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);
|
||||
|
||||
friend
|
||||
void
|
||||
teardown(websocket::role_type,
|
||||
stream& s, boost::system::error_code& ec);
|
||||
|
||||
template<class TeardownHandler>
|
||||
friend
|
||||
void
|
||||
async_teardown(websocket::role_type role,
|
||||
stream& s, TeardownHandler&& handler);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
inline
|
||||
void
|
||||
stream::
|
||||
clear()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock{in_->m};
|
||||
in_->b.consume(in_->b.size());
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
@ -532,7 +304,7 @@ async_read_some(
|
||||
}
|
||||
else
|
||||
{
|
||||
in_->op.reset(new read_op_impl<BOOST_ASIO_HANDLER_TYPE(
|
||||
in_->op.reset(new read_op<BOOST_ASIO_HANDLER_TYPE(
|
||||
ReadHandler, void(error_code, std::size_t)),
|
||||
MutableBufferSequence>{*in_, buffers,
|
||||
std::move(init.completion_handler)});
|
||||
@ -645,9 +417,9 @@ async_write_some(ConstBufferSequence const& buffers,
|
||||
inline
|
||||
void
|
||||
teardown(
|
||||
websocket::role_type,
|
||||
stream& s,
|
||||
boost::system::error_code& ec)
|
||||
websocket::role_type,
|
||||
stream& s,
|
||||
boost::system::error_code& ec)
|
||||
{
|
||||
if( s.in_->fc &&
|
||||
s.in_->fc->fail(ec))
|
||||
@ -666,9 +438,9 @@ template<class TeardownHandler>
|
||||
inline
|
||||
void
|
||||
async_teardown(
|
||||
websocket::role_type,
|
||||
stream& s,
|
||||
TeardownHandler&& handler)
|
||||
websocket::role_type,
|
||||
stream& s,
|
||||
TeardownHandler&& handler)
|
||||
{
|
||||
error_code ec;
|
||||
if( s.in_->fc &&
|
||||
@ -691,7 +463,7 @@ async_teardown(
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Handler, class Buffers>
|
||||
class stream::read_op_impl : public stream::read_op
|
||||
class stream::read_op : public stream::read_op_base
|
||||
{
|
||||
class lambda
|
||||
{
|
||||
@ -769,7 +541,7 @@ class stream::read_op_impl : public stream::read_op
|
||||
|
||||
public:
|
||||
template<class DeducedHandler>
|
||||
read_op_impl(state& s, Buffers const& b, DeducedHandler&& h)
|
||||
read_op(state& s, Buffers const& b, DeducedHandler&& h)
|
||||
: fn_(s, b, std::forward<DeducedHandler>(h))
|
||||
{
|
||||
}
|
||||
@ -781,7 +553,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/// Create and return a connected stream
|
||||
inline
|
||||
stream
|
||||
connect(stream& to)
|
||||
@ -791,7 +562,6 @@ connect(stream& to)
|
||||
return from;
|
||||
}
|
||||
|
||||
/// Create and return a connected stream
|
||||
template<class Arg1, class... ArgN>
|
||||
stream
|
||||
connect(stream& to, Arg1&& arg1, ArgN&&... argn)
|
538
include/boost/beast/experimental/test/stream.hpp
Normal file
538
include/boost/beast/experimental/test/stream.hpp
Normal file
@ -0,0 +1,538 @@
|
||||
//
|
||||
// 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_TEST_STREAM_HPP
|
||||
#define BOOST_BEAST_TEST_STREAM_HPP
|
||||
|
||||
#include <boost/beast/core/bind_handler.hpp>
|
||||
#include <boost/beast/core/flat_buffer.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/beast/websocket/teardown.hpp>
|
||||
#include <boost/beast/experimental/test/fail_count.hpp>
|
||||
#include <boost/asio/async_result.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/asio/executor_work_guard.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <condition_variable>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace test {
|
||||
|
||||
/** A two-way socket useful for unit testing
|
||||
|
||||
An instance of this class simulates a traditional socket,
|
||||
while also providing features useful for unit testing.
|
||||
Each endpoint maintains an independent buffer called
|
||||
the input area. Writes from one endpoint append data
|
||||
to the peer's pending input area. When an endpoint performs
|
||||
a read and data is present in the input area, the data is
|
||||
delivered to the blocking or asynchronous operation. Otherwise
|
||||
the operation is blocked or deferred until data is made
|
||||
available, or until the endpoints become disconnected.
|
||||
|
||||
These streams may be used anywhere an algorithm accepts a
|
||||
reference to a synchronous or asynchronous read or write
|
||||
stream. It is possible to use a test stream in a call to
|
||||
`boost::asio::read_until`, or in a call to
|
||||
@ref boost::beast::http::async_write for example.
|
||||
|
||||
As with Boost.Asio I/O objects, a @ref stream constructs
|
||||
with a reference to the `boost::asio::io_context` to use for
|
||||
handling asynchronous I/O. For asynchronous operations, the
|
||||
stream follows the same rules as a traditional asio socket
|
||||
with respect to how completion handlers for asynchronous
|
||||
operations are performed.
|
||||
|
||||
To facilitate testing, these streams support some additional
|
||||
features:
|
||||
|
||||
@li The input area, represented by a @ref flat_buffer, may
|
||||
be directly accessed by the caller to inspect the contents
|
||||
before or after the remote endpoint writes data. This allows
|
||||
a unit test to verify that the received data matches.
|
||||
|
||||
@li Data may be manually appended to the input area. This data
|
||||
will delivered in the next call to
|
||||
@ref stream::read_some or @ref stream::async_read_some.
|
||||
This allows predefined test vectors to be set up for testing
|
||||
read algorithms.
|
||||
|
||||
@li The stream may be constructed with a @ref fail count. The
|
||||
stream will eventually fail with a predefined error after a
|
||||
certain number of operations, where the number of operations
|
||||
is controlled by the test. When a test loops over a range of
|
||||
operation counts, it is possible to exercise every possible
|
||||
point of failure in the algorithm being tested. When used
|
||||
correctly the technique allows the tests to reach a high
|
||||
percentage of code coverage.
|
||||
|
||||
@par Thread Safety
|
||||
@e Distinct @e objects: Safe.@n
|
||||
@e Shared @e objects: Unsafe.
|
||||
The application must also ensure that all asynchronous
|
||||
operations are performed within the same implicit or explicit strand.
|
||||
|
||||
@par Concepts
|
||||
@li @b SyncReadStream
|
||||
@li @b SyncWriteStream
|
||||
@li @b AsyncReadStream
|
||||
@li @b AsyncWriteStream
|
||||
*/
|
||||
class stream
|
||||
{
|
||||
struct read_op_base
|
||||
{
|
||||
virtual ~read_op_base() = default;
|
||||
virtual void operator()() = 0;
|
||||
};
|
||||
|
||||
template<class Handler, class Buffers>
|
||||
class read_op;
|
||||
|
||||
enum class status
|
||||
{
|
||||
ok,
|
||||
eof,
|
||||
reset
|
||||
};
|
||||
|
||||
struct state
|
||||
{
|
||||
friend class stream;
|
||||
|
||||
std::mutex m;
|
||||
flat_buffer b;
|
||||
std::condition_variable cv;
|
||||
std::unique_ptr<read_op_base> op;
|
||||
boost::asio::io_context& ioc;
|
||||
status code = status::ok;
|
||||
fail_count* fc = nullptr;
|
||||
std::size_t nread = 0;
|
||||
std::size_t nwrite = 0;
|
||||
std::size_t read_max =
|
||||
(std::numeric_limits<std::size_t>::max)();
|
||||
std::size_t write_max =
|
||||
(std::numeric_limits<std::size_t>::max)();
|
||||
|
||||
~state()
|
||||
{
|
||||
BOOST_ASSERT(! op);
|
||||
}
|
||||
|
||||
explicit
|
||||
state(
|
||||
boost::asio::io_context& ioc_,
|
||||
fail_count* fc_)
|
||||
: ioc(ioc_)
|
||||
, fc(fc_)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
on_write()
|
||||
{
|
||||
if(op)
|
||||
{
|
||||
std::unique_ptr<read_op_base> op_ = std::move(op);
|
||||
op_->operator()();
|
||||
}
|
||||
else
|
||||
{
|
||||
cv.notify_all();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<state> in_;
|
||||
std::weak_ptr<state> out_;
|
||||
|
||||
public:
|
||||
using buffer_type = flat_buffer;
|
||||
|
||||
/// The type of the lowest layer.
|
||||
using lowest_layer_type = stream;
|
||||
|
||||
/** Destructor
|
||||
|
||||
If an asynchronous read operation is pending, it will
|
||||
simply be discarded with no notification to the completion
|
||||
handler.
|
||||
|
||||
If a connection is established while the stream is destroyed,
|
||||
the peer will see the error `boost::asio::error::connection_reset`
|
||||
when performing any reads or writes.
|
||||
*/
|
||||
~stream();
|
||||
|
||||
/** Move Constructor
|
||||
|
||||
Moving the stream while asynchronous operations are pending
|
||||
results in undefined behavior.
|
||||
*/
|
||||
stream(stream&& other);
|
||||
|
||||
/** Move Assignment
|
||||
|
||||
Moving the stream while asynchronous operations are pending
|
||||
results in undefined behavior.
|
||||
*/
|
||||
stream&
|
||||
operator=(stream&& other);
|
||||
|
||||
/** Construct a stream
|
||||
|
||||
The stream will be created in a disconnected state.
|
||||
|
||||
@param ioc The `io_context` object that the stream will use to
|
||||
dispatch handlers for any asynchronous operations.
|
||||
*/
|
||||
explicit
|
||||
stream(boost::asio::io_context& ioc);
|
||||
|
||||
/** Construct a stream
|
||||
|
||||
The stream will be created in a disconnected state.
|
||||
|
||||
@param ioc The `io_context` object that the stream will use to
|
||||
dispatch handlers for any asynchronous operations.
|
||||
|
||||
@param fc The @ref fail_count to associate with the stream.
|
||||
Each I/O operation performed on the stream will increment the
|
||||
fail count. When the fail count reaches its internal limit,
|
||||
a simulated failure error will be raised.
|
||||
*/
|
||||
stream(
|
||||
boost::asio::io_context& ioc,
|
||||
fail_count& fc);
|
||||
|
||||
/** Construct a stream
|
||||
|
||||
The stream will be created in a disconnected state.
|
||||
|
||||
@param ioc The `io_context` object that the stream will use to
|
||||
dispatch handlers for any asynchronous operations.
|
||||
|
||||
@param s A string which will be appended to the input area, not
|
||||
including the null terminator.
|
||||
*/
|
||||
stream(
|
||||
boost::asio::io_context& ioc,
|
||||
string_view s);
|
||||
|
||||
/** Construct a stream
|
||||
|
||||
The stream will be created in a disconnected state.
|
||||
|
||||
@param ioc The `io_context` object that the stream will use to
|
||||
dispatch handlers for any asynchronous operations.
|
||||
|
||||
@param fc The @ref fail_count to associate with the stream.
|
||||
Each I/O operation performed on the stream will increment the
|
||||
fail count. When the fail count reaches its internal limit,
|
||||
a simulated failure error will be raised.
|
||||
|
||||
@param s A string which will be appended to the input area, not
|
||||
including the null terminator.
|
||||
*/
|
||||
stream(
|
||||
boost::asio::io_context& ioc,
|
||||
fail_count& fc,
|
||||
string_view s);
|
||||
|
||||
/// Establish a connection
|
||||
void
|
||||
connect(stream& remote);
|
||||
|
||||
/// The type of the executor associated with the object.
|
||||
using executor_type =
|
||||
boost::asio::io_context::executor_type;
|
||||
|
||||
/// Return the executor associated with the object.
|
||||
boost::asio::io_context::executor_type
|
||||
get_executor() noexcept
|
||||
{
|
||||
return in_->ioc.get_executor();
|
||||
};
|
||||
|
||||
/** 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 *this;
|
||||
}
|
||||
|
||||
/** 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 *this;
|
||||
}
|
||||
|
||||
/// Set the maximum number of bytes returned by read_some
|
||||
void
|
||||
read_size(std::size_t n)
|
||||
{
|
||||
in_->read_max = n;
|
||||
}
|
||||
|
||||
/// Set the maximum number of bytes returned by write_some
|
||||
void
|
||||
write_size(std::size_t n)
|
||||
{
|
||||
in_->write_max = n;
|
||||
}
|
||||
|
||||
/// Direct input buffer access
|
||||
buffer_type&
|
||||
buffer()
|
||||
{
|
||||
return in_->b;
|
||||
}
|
||||
|
||||
/// Returns a string view representing the pending input data
|
||||
string_view
|
||||
str() const;
|
||||
|
||||
/// Appends a string to the pending input data
|
||||
void
|
||||
append(string_view s);
|
||||
|
||||
/// Clear the pending input area
|
||||
void
|
||||
clear();
|
||||
|
||||
/// Return the number of reads
|
||||
std::size_t
|
||||
nread() const
|
||||
{
|
||||
return in_->nread;
|
||||
}
|
||||
|
||||
/// Return the number of writes
|
||||
std::size_t
|
||||
nwrite() const
|
||||
{
|
||||
return in_->nwrite;
|
||||
}
|
||||
|
||||
/** Close the stream.
|
||||
|
||||
The other end of the connection will see
|
||||
`error::eof` after reading all the remaining data.
|
||||
*/
|
||||
void
|
||||
close();
|
||||
|
||||
/** Close the other end of the stream.
|
||||
|
||||
This end of the connection will see
|
||||
`error::eof` after reading all the remaining data.
|
||||
*/
|
||||
void
|
||||
close_remote();
|
||||
|
||||
/** 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&);
|
||||
|
||||
/** 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);
|
||||
|
||||
#ifndef BOOST_BEAST_DOXYGEN
|
||||
friend
|
||||
void
|
||||
teardown(
|
||||
websocket::role_type,
|
||||
stream& s,
|
||||
boost::system::error_code& ec);
|
||||
|
||||
template<class TeardownHandler>
|
||||
friend
|
||||
void
|
||||
async_teardown(
|
||||
websocket::role_type role,
|
||||
stream& s,
|
||||
TeardownHandler&& handler);
|
||||
#endif
|
||||
};
|
||||
|
||||
/// Create and return a connected stream
|
||||
stream
|
||||
connect(stream& to);
|
||||
|
||||
/// Create and return a connected stream
|
||||
template<class Arg1, class... ArgN>
|
||||
stream
|
||||
connect(stream& to, Arg1&& arg1, ArgN&&... argn);
|
||||
|
||||
} // test
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/experimental/test/impl/stream.ipp>
|
||||
|
||||
#endif
|
@ -11,7 +11,7 @@
|
||||
#include <boost/beast/core/bind_handler.hpp>
|
||||
|
||||
#include <boost/beast/core/detail/type_traits.hpp>
|
||||
#include <boost/beast/test/stream.hpp>
|
||||
#include <boost/beast/experimental/test/stream.hpp>
|
||||
#include <boost/beast/unit_test/suite.hpp>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <boost/asio/strand.hpp>
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include <boost/beast/core/buffered_read_stream.hpp>
|
||||
|
||||
#include <boost/beast/core/multi_buffer.hpp>
|
||||
#include <boost/beast/test/stream.hpp>
|
||||
#include <boost/beast/experimental/test/stream.hpp>
|
||||
#include <boost/beast/test/yield_to.hpp>
|
||||
#include <boost/beast/unit_test/suite.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
|
@ -20,6 +20,7 @@ add_executable (tests-beast-experimental
|
||||
error.cpp
|
||||
flat_stream.cpp
|
||||
ssl_stream.cpp
|
||||
stream.cpp
|
||||
)
|
||||
|
||||
set_property(TARGET tests-beast-experimental PROPERTY FOLDER "tests")
|
||||
|
@ -11,6 +11,7 @@ local SOURCES =
|
||||
error.cpp
|
||||
flat_stream.cpp
|
||||
ssl_stream.cpp
|
||||
stream.cpp
|
||||
;
|
||||
|
||||
local RUN_TESTS ;
|
||||
|
11
test/beast/experimental/stream.cpp
Normal file
11
test/beast/experimental/stream.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
// Test that header file is self-contained.
|
||||
#include <boost/beast/experimental/test/stream.hpp>
|
@ -16,7 +16,7 @@
|
||||
#include <boost/beast/http/parser.hpp>
|
||||
#include <boost/beast/http/read.hpp>
|
||||
#include <boost/beast/http/write.hpp>
|
||||
#include <boost/beast/test/stream.hpp>
|
||||
#include <boost/beast/experimental/test/stream.hpp>
|
||||
#include <boost/beast/unit_test/suite.hpp>
|
||||
|
||||
namespace boost {
|
||||
|
@ -18,7 +18,7 @@
|
||||
#include <boost/beast/http/dynamic_body.hpp>
|
||||
#include <boost/beast/http/parser.hpp>
|
||||
#include <boost/beast/http/string_body.hpp>
|
||||
#include <boost/beast/test/stream.hpp>
|
||||
#include <boost/beast/experimental/test/stream.hpp>
|
||||
#include <boost/beast/test/yield_to.hpp>
|
||||
#include <boost/beast/unit_test/suite.hpp>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include <boost/beast/core/buffers_to_string.hpp>
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/beast/core/multi_buffer.hpp>
|
||||
#include <boost/beast/test/stream.hpp>
|
||||
#include <boost/beast/experimental/test/stream.hpp>
|
||||
#include <boost/beast/test/yield_to.hpp>
|
||||
#include <boost/beast/unit_test/suite.hpp>
|
||||
#include <boost/asio/error.hpp>
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include <boost/beast/core/ostream.hpp>
|
||||
#include <boost/beast/core/multi_buffer.hpp>
|
||||
#include <boost/beast/websocket/stream.hpp>
|
||||
#include <boost/beast/test/stream.hpp>
|
||||
#include <boost/beast/experimental/test/stream.hpp>
|
||||
#include <boost/beast/test/yield_to.hpp>
|
||||
#include <boost/beast/unit_test/suite.hpp>
|
||||
#include <boost/asio/executor_work_guard.hpp>
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
#include <boost/beast/core/flat_buffer.hpp>
|
||||
#include <boost/beast/core/ostream.hpp>
|
||||
#include <boost/beast/test/stream.hpp>
|
||||
#include <boost/beast/experimental/test/stream.hpp>
|
||||
#include <boost/beast/test/yield_to.hpp>
|
||||
#include <boost/beast/unit_test/suite.hpp>
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
#include <boost/beast/http/parser.hpp>
|
||||
#include <boost/beast/http/read.hpp>
|
||||
#include <boost/beast/http/write.hpp>
|
||||
#include <boost/beast/test/stream.hpp>
|
||||
#include <boost/beast/experimental/test/stream.hpp>
|
||||
#include <boost/beast/test/yield_to.hpp>
|
||||
#include <boost/beast/unit_test/suite.hpp>
|
||||
#include <sstream>
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
#include <boost/beast/core/multi_buffer.hpp>
|
||||
#include <boost/beast/websocket/stream.hpp>
|
||||
#include <boost/beast/test/stream.hpp>
|
||||
#include <boost/beast/experimental/test/stream.hpp>
|
||||
#include <boost/asio/executor_work_guard.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/spawn.hpp>
|
||||
|
Reference in New Issue
Block a user