Use test::stream

This commit is contained in:
Vinnie Falco committed 2017-08-16 18:26:48 -07:00
1 parent e690528132
commit 2c73eeef7c
16 files changed
+310 -1563

No files matched your search

@@ -1,200 +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_TEST_FAIL_STREAM_HPP
#define BOOST_BEAST_TEST_FAIL_STREAM_HPP
#include <boost/beast/core/async_result.hpp>
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/beast/core/detail/type_traits.hpp>
#include <boost/beast/websocket/teardown.hpp>
#include <boost/beast/test/fail_counter.hpp>
#include <boost/optional.hpp>
namespace boost {
namespace beast {
namespace test {
/** A stream wrapper that fails.
On the Nth operation, the stream will fail with the specified
error code, or the default error code of invalid_argument.
*/
template<class NextLayer>
class fail_stream
{
boost::optional<fail_counter> fc_;
fail_counter* pfc_;
NextLayer next_layer_;
public:
using next_layer_type =
typename std::remove_reference<NextLayer>::type;
using lowest_layer_type =
typename get_lowest_layer<next_layer_type>::type;
fail_stream(fail_stream&&) = delete;
fail_stream(fail_stream const&) = delete;
fail_stream& operator=(fail_stream&&) = delete;
fail_stream& operator=(fail_stream const&) = delete;
template<class... Args>
explicit
fail_stream(std::size_t n, Args&&... args)
: fc_(n)
, pfc_(&*fc_)
, next_layer_(std::forward<Args>(args)...)
{
}
template<class... Args>
explicit
fail_stream(fail_counter& fc, Args&&... args)
: pfc_(&fc)
, next_layer_(std::forward<Args>(args)...)
{
}
next_layer_type&
next_layer()
{
return next_layer_;
}
lowest_layer_type&
lowest_layer()
{
return next_layer_.lowest_layer();
}
lowest_layer_type const&
lowest_layer() const
{
return next_layer_.lowest_layer();
}
boost::asio::io_service&
get_io_service()
{
return next_layer_.get_io_service();
}
template<class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers)
{
pfc_->fail();
return next_layer_.read_some(buffers);
}
template<class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers, error_code& ec)
{
if(pfc_->fail(ec))
return 0;
return next_layer_.read_some(buffers, ec);
}
template<class MutableBufferSequence, class ReadHandler>
async_return_type<
ReadHandler, void(error_code, std::size_t)>
async_read_some(MutableBufferSequence const& buffers,
ReadHandler&& handler)
{
error_code ec;
if(pfc_->fail(ec))
{
async_completion<ReadHandler,
void(error_code, std::size_t)> init{handler};
next_layer_.get_io_service().post(
bind_handler(init.completion_handler, ec, 0));
return init.result.get();
}
return next_layer_.async_read_some(buffers,
std::forward<ReadHandler>(handler));
}
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers)
{
pfc_->fail();
return next_layer_.write_some(buffers);
}
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers, error_code& ec)
{
if(pfc_->fail(ec))
return 0;
return next_layer_.write_some(buffers, ec);
}
template<class ConstBufferSequence, class WriteHandler>
async_return_type<
WriteHandler, void(error_code, std::size_t)>
async_write_some(ConstBufferSequence const& buffers,
WriteHandler&& handler)
{
error_code ec;
if(pfc_->fail(ec))
{
async_completion<WriteHandler,
void(error_code, std::size_t)> init{handler};
next_layer_.get_io_service().post(
bind_handler(init.completion_handler, ec, 0));
return init.result.get();
}
return next_layer_.async_write_some(buffers,
std::forward<WriteHandler>(handler));
}
friend
void
teardown(
websocket::role_type role,
fail_stream<NextLayer>& stream,
boost::system::error_code& ec)
{
if(stream.pfc_->fail(ec))
return;
using beast::websocket::teardown;
teardown(role, stream.next_layer(), ec);
}
template<class TeardownHandler>
friend
void
async_teardown(
websocket::role_type role,
fail_stream<NextLayer>& stream,
TeardownHandler&& handler)
{
error_code ec;
if(stream.pfc_->fail(ec))
{
stream.get_io_service().post(
bind_handler(std::move(handler), ec));
return;
}
using beast::websocket::async_teardown;
async_teardown(role, stream.next_layer(),
std::forward<TeardownHandler>(handler));
}
};
} // test
} // beast
} // boost
#endif
@@ -1,553 +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_TEST_PIPE_STREAM_HPP
#define BOOST_BEAST_TEST_PIPE_STREAM_HPP
#include <boost/beast/core/async_result.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/test/fail_counter.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/assert.hpp>
#include <boost/throw_exception.hpp>
#include <condition_variable>
#include <limits>
#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 pipe
{
public:
using buffer_type = flat_buffer;
private:
struct read_op
{
virtual ~read_op() = default;
virtual void operator()() = 0;
};
struct state
{
std::mutex m;
buffer_type b;
std::condition_variable cv;
std::unique_ptr<read_op> op;
bool eof = false;
};
state s_[2];
public:
/** Represents an endpoint.
Each pipe has a client stream and a server stream.
*/
class stream
{
friend class pipe;
template<class Handler, class Buffers>
class read_op_impl;
state& in_;
state& out_;
boost::asio::io_service& ios_;
fail_counter* fc_ = nullptr;
std::size_t read_max_ =
(std::numeric_limits<std::size_t>::max)();
std::size_t write_max_ =
(std::numeric_limits<std::size_t>::max)();
stream(state& in, state& out,
boost::asio::io_service& ios)
: in_(in)
, out_(out)
, ios_(ios)
, buffer(in_.b)
{
}
public:
using buffer_type = pipe::buffer_type;
/// Direct access to the underlying buffer
buffer_type& buffer;
/// Counts the number of read calls
std::size_t nread = 0;
/// Counts the number of write calls
std::size_t nwrite = 0;
~stream() = default;
stream(stream&&) = default;
/// Set the fail counter on the object
#if 0
void
fail(fail_counter& fc)
{
fc_ = &fc;
}
#endif
/// Return the `io_service` associated with the object
boost::asio::io_service&
get_io_service()
{
return ios_;
}
/// Set the maximum number of bytes returned by read_some
void
read_size(std::size_t n)
{
read_max_ = n;
}
/// Set the maximum number of bytes returned by write_some
void
write_size(std::size_t n)
{
write_max_ = n;
}
/// Returns a string representing the pending input data
string_view
str() const
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
return {
buffer_cast<char const*>(*in_.b.data().begin()),
buffer_size(*in_.b.data().begin())};
}
/// Clear the buffer holding the input data
void
clear()
{
in_.b.consume((std::numeric_limits<
std::size_t>::max)());
}
/** Close the stream.
The other end of the pipe will see
`boost::asio::error::eof` on read.
*/
template<class = void>
void
close();
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>
async_return_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>
async_return_type<
WriteHandler, void(error_code, std::size_t)>
async_write_some(ConstBufferSequence const& buffers,
WriteHandler&& handler);
friend
void
teardown(
websocket::role_type role,
stream& s, boost::system::error_code& ec);
template<class TeardownHandler>
friend
void
async_teardown(
websocket::role_type,
stream& s, TeardownHandler&& handler);
};
/** Constructor.
The client and server endpoints will use the same `io_service`.
*/
explicit
pipe(boost::asio::io_service& ios)
: client(s_[0], s_[1], ios)
, server(s_[1], s_[0], ios)
{
}
/// Represents the client endpoint
stream client;
/// Represents the server endpoint
stream server;
};
//------------------------------------------------------------------------------
inline
void
teardown(
websocket::role_type,
pipe::stream& s,
boost::system::error_code& ec)
{
if(s.fc_)
{
if(s.fc_->fail(ec))
return;
}
else
{
s.close();
ec.assign(0, ec.category());
}
}
template<class TeardownHandler>
inline
void
async_teardown(websocket::role_type,
pipe::stream& s, TeardownHandler&& handler)
{
error_code ec;
if(s.fc_ && s.fc_->fail(ec))
return s.get_io_service().post(
bind_handler(std::move(handler), ec));
s.close();
s.get_io_service().post(
bind_handler(std::move(handler), ec));
}
template<class Handler, class Buffers>
class pipe::stream::read_op_impl :
public pipe::read_op
{
stream& s_;
Buffers b_;
Handler h_;
public:
read_op_impl(stream& s,
Buffers const& b, Handler&& h)
: s_(s)
, b_(b)
, h_(std::move(h))
{
}
read_op_impl(stream& s,
Buffers const& b, Handler const& h)
: s_(s)
, b_(b)
, h_(h)
{
}
void
operator()() override;
};
//------------------------------------------------------------------------------
template<class Handler, class Buffers>
void
pipe::stream::
read_op_impl<Handler, Buffers>::operator()()
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
s_.ios_.post(
[&]()
{
BOOST_ASSERT(s_.in_.op);
std::unique_lock<std::mutex> lock{s_.in_.m};
if(s_.in_.b.size() > 0)
{
auto const bytes_transferred = buffer_copy(
b_, s_.in_.b.data(), s_.read_max_);
s_.in_.b.consume(bytes_transferred);
auto& s = s_;
Handler h{std::move(h_)};
s.in_.op.reset(nullptr);
lock.unlock();
++s.nread;
s.ios_.post(bind_handler(std::move(h),
error_code{}, bytes_transferred));
}
else
{
BOOST_ASSERT(s_.in_.eof);
auto& s = s_;
Handler h{std::move(h_)};
s.in_.op.reset(nullptr);
lock.unlock();
++s.nread;
s.ios_.post(bind_handler(std::move(h),
boost::asio::error::eof, 0));
}
});
}
//------------------------------------------------------------------------------
template<class>
void
pipe::stream::
close()
{
std::lock_guard<std::mutex> lock{out_.m};
if(! out_.eof)
{
out_.eof = true;
if(out_.op)
out_.op.get()->operator()();
else
out_.cv.notify_all();
}
}
template<class MutableBufferSequence>
std::size_t
pipe::stream::
read_some(MutableBufferSequence const& buffers)
{
static_assert(is_mutable_buffer_sequence<
MutableBufferSequence>::value,
"MutableBufferSequence requirements not met");
error_code ec;
auto const n = read_some(buffers, ec);
if(ec)
BOOST_THROW_EXCEPTION(system_error{ec});
return n;
}
template<class MutableBufferSequence>
std::size_t
pipe::stream::
read_some(MutableBufferSequence const& buffers,
error_code& ec)
{
static_assert(is_mutable_buffer_sequence<
MutableBufferSequence>::value,
"MutableBufferSequence requirements not met");
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
BOOST_ASSERT(! in_.op);
BOOST_ASSERT(buffer_size(buffers) > 0);
if(fc_ && fc_->fail(ec))
return 0;
std::unique_lock<std::mutex> lock{in_.m};
in_.cv.wait(lock,
[&]()
{
return in_.b.size() > 0 || in_.eof;
});
std::size_t bytes_transferred;
if(in_.b.size() > 0)
{
ec.assign(0, ec.category());
bytes_transferred = buffer_copy(
buffers, in_.b.data(), read_max_);
in_.b.consume(bytes_transferred);
}
else
{
BOOST_ASSERT(in_.eof);
bytes_transferred = 0;
ec = boost::asio::error::eof;
}
++nread;
return bytes_transferred;
}
template<class MutableBufferSequence, class ReadHandler>
async_return_type<
ReadHandler, void(error_code, std::size_t)>
pipe::stream::
async_read_some(MutableBufferSequence const& buffers,
ReadHandler&& handler)
{
static_assert(is_mutable_buffer_sequence<
MutableBufferSequence>::value,
"MutableBufferSequence requirements not met");
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
BOOST_ASSERT(! in_.op);
BOOST_ASSERT(buffer_size(buffers) > 0);
async_completion<ReadHandler,
void(error_code, std::size_t)> init{handler};
if(fc_)
{
error_code ec;
if(fc_->fail(ec))
return ios_.post(bind_handler(
init.completion_handler, ec, 0));
}
{
std::unique_lock<std::mutex> lock{in_.m};
if(in_.eof)
{
lock.unlock();
++nread;
ios_.post(bind_handler(init.completion_handler,
boost::asio::error::eof, 0));
}
else if(buffer_size(buffers) == 0 ||
buffer_size(in_.b.data()) > 0)
{
auto const bytes_transferred = buffer_copy(
buffers, in_.b.data(), read_max_);
in_.b.consume(bytes_transferred);
lock.unlock();
++nread;
ios_.post(bind_handler(init.completion_handler,
error_code{}, bytes_transferred));
}
else
{
in_.op.reset(new read_op_impl<handler_type<
ReadHandler, void(error_code, std::size_t)>,
MutableBufferSequence>{*this, buffers,
init.completion_handler});
}
}
return init.result.get();
}
template<class ConstBufferSequence>
std::size_t
pipe::stream::
write_some(ConstBufferSequence const& buffers)
{
static_assert(is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
BOOST_ASSERT(! out_.eof);
error_code ec;
auto const bytes_transferred =
write_some(buffers, ec);
if(ec)
BOOST_THROW_EXCEPTION(system_error{ec});
return bytes_transferred;
}
template<class ConstBufferSequence>
std::size_t
pipe::stream::
write_some(
ConstBufferSequence const& buffers, error_code& ec)
{
static_assert(is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
BOOST_ASSERT(! out_.eof);
if(fc_ && fc_->fail(ec))
return 0;
auto const n = (std::min)(
buffer_size(buffers), write_max_);
std::unique_lock<std::mutex> lock{out_.m};
auto const bytes_transferred =
buffer_copy(out_.b.prepare(n), buffers);
out_.b.commit(bytes_transferred);
if(out_.op)
out_.op.get()->operator()();
else
out_.cv.notify_all();
lock.unlock();
++nwrite;
ec.assign(0, ec.category());
return bytes_transferred;
}
template<class ConstBufferSequence, class WriteHandler>
async_return_type<
WriteHandler, void(error_code, std::size_t)>
pipe::stream::
async_write_some(ConstBufferSequence const& buffers,
WriteHandler&& handler)
{
static_assert(is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
BOOST_ASSERT(! out_.eof);
async_completion<WriteHandler,
void(error_code, std::size_t)> init{handler};
if(fc_)
{
error_code ec;
if(fc_->fail(ec))
return ios_.post(bind_handler(
init.completion_handler, ec, 0));
}
auto const n =
(std::min)(buffer_size(buffers), write_max_);
std::unique_lock<std::mutex> lock{out_.m};
auto const bytes_transferred =
buffer_copy(out_.b.prepare(n), buffers);
out_.b.commit(bytes_transferred);
if(out_.op)
out_.op.get()->operator()();
else
out_.cv.notify_all();
lock.unlock();
++nwrite;
ios_.post(bind_handler(init.completion_handler,
error_code{}, bytes_transferred));
return init.result.get();
}
} // test
} // beast
} // boost
#endif
+104 -94
View File
@@ -239,38 +239,40 @@ class stream
using status = detail::stream_impl::status;
std::shared_ptr<detail::stream_impl> impl_;
detail::stream_impl::state& in_;
detail::stream_impl::state& out_;
detail::stream_impl::state* in_;
detail::stream_impl::state* out_;
explicit
stream(std::shared_ptr<
detail::stream_impl> const& impl)
: impl_(impl)
, in_(impl_->s1_)
, out_(impl_->s0_)
, in_(&impl_->s1_)
, out_(&impl_->s0_)
{
}
public:
using buffer_type = flat_buffer;
stream& operator=(stream const&) = delete;
/// Assignment
stream& operator=(stream&&) = default;
/// Destructor
~stream()
{
if(! impl_)
return;
BOOST_ASSERT(! in_.op);
std::unique_lock<std::mutex> lock{out_.m};
if(out_.code == status::ok)
BOOST_ASSERT(! in_->op);
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();
}
lock.unlock();
}
/// Constructor
stream(stream&& other)
: impl_(std::move(other.impl_))
, in_(other.in_)
@@ -284,8 +286,8 @@ public:
boost::asio::io_service& ios)
: impl_(std::make_shared<
detail::stream_impl>(ios, nullptr))
, in_(impl_->s0_)
, out_(impl_->s1_)
, in_(&impl_->s0_)
, out_(&impl_->s1_)
{
}
@@ -296,8 +298,8 @@ public:
boost::asio::io_service& ios1)
: impl_(std::make_shared<
detail::stream_impl>(ios0, ios1))
, in_(impl_->s0_)
, out_(impl_->s1_)
, in_(&impl_->s0_)
, out_(&impl_->s1_)
{
}
@@ -308,8 +310,8 @@ public:
fail_counter& fc)
: impl_(std::make_shared<
detail::stream_impl>(ios, &fc))
, in_(impl_->s0_)
, out_(impl_->s1_)
, in_(&impl_->s0_)
, out_(&impl_->s1_)
{
}
@@ -319,13 +321,13 @@ public:
string_view s)
: impl_(std::make_shared<
detail::stream_impl>(ios, nullptr))
, in_(impl_->s0_)
, out_(impl_->s1_)
, in_(&impl_->s0_)
, out_(&impl_->s1_)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
in_.b.commit(buffer_copy(
in_.b.prepare(s.size()),
in_->b.commit(buffer_copy(
in_->b.prepare(s.size()),
buffer(s.data(), s.size())));
}
@@ -336,13 +338,13 @@ public:
string_view s)
: impl_(std::make_shared<
detail::stream_impl>(ios, &fc))
, in_(impl_->s0_)
, out_(impl_->s1_)
, in_(&impl_->s0_)
, out_(&impl_->s1_)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
in_.b.commit(buffer_copy(
in_.b.prepare(s.size()),
in_->b.commit(buffer_copy(
in_->b.prepare(s.size()),
buffer(s.data(), s.size())));
}
@@ -350,7 +352,7 @@ public:
stream
remote()
{
BOOST_ASSERT(&in_ == &impl_->s0_);
BOOST_ASSERT(in_ == &impl_->s0_);
return stream{impl_};
}
@@ -358,28 +360,28 @@ public:
boost::asio::io_service&
get_io_service()
{
return in_.ios;
return in_->ios;
}
/// Set the maximum number of bytes returned by read_some
void
read_size(std::size_t n)
{
in_.read_max = n;
in_->read_max = n;
}
/// Set the maximum number of bytes returned by write_some
void
write_size(std::size_t n)
{
out_.write_max = n;
out_->write_max = n;
}
/// Direct input buffer access
buffer_type&
buffer()
{
return in_.b;
return in_->b;
}
/// Returns a string view representing the pending input data
@@ -389,8 +391,8 @@ public:
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
return {
buffer_cast<char const*>(*in_.b.data().begin()),
buffer_size(*in_.b.data().begin())};
buffer_cast<char const*>(*in_->b.data().begin()),
buffer_size(*in_->b.data().begin())};
}
/// Appends a string to the pending input data
@@ -399,24 +401,32 @@ public:
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
std::unique_lock<std::mutex> lock{in_.m};
in_.b.commit(buffer_copy(
in_.b.prepare(s.size()),
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 in_->nread;
}
/// Return the number of writes
std::size_t
nwrite() const
{
return out_.nwrite;
return out_->nwrite;
}
/** Close the stream.
@@ -499,35 +509,35 @@ read_some(MutableBufferSequence const& buffers,
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
BOOST_ASSERT(buffer_size(buffers) > 0);
if(in_.fc && in_.fc->fail(ec))
if(in_->fc && in_->fc->fail(ec))
return 0;
std::unique_lock<std::mutex> lock{in_.m};
BOOST_ASSERT(! in_.op);
in_.cv.wait(lock,
std::unique_lock<std::mutex> lock{in_->m};
BOOST_ASSERT(! in_->op);
in_->cv.wait(lock,
[&]()
{
return
in_.b.size() > 0 ||
in_.code != status::ok;
in_->b.size() > 0 ||
in_->code != status::ok;
});
std::size_t bytes_transferred;
if(in_.b.size() > 0)
if(in_->b.size() > 0)
{
ec.assign(0, ec.category());
bytes_transferred = buffer_copy(
buffers, in_.b.data(), in_.read_max);
in_.b.consume(bytes_transferred);
buffers, in_->b.data(), in_->read_max);
in_->b.consume(bytes_transferred);
}
else
{
BOOST_ASSERT(in_.code != status::ok);
BOOST_ASSERT(in_->code != status::ok);
bytes_transferred = 0;
if(in_.code == status::eof)
if(in_->code == status::eof)
ec = boost::asio::error::eof;
else if(in_.code == status::reset)
else if(in_->code == status::reset)
ec = boost::asio::error::connection_reset;
}
++in_.nread;
++in_->nread;
return bytes_transferred;
}
@@ -547,45 +557,45 @@ async_read_some(
BOOST_ASSERT(buffer_size(buffers) > 0);
async_completion<ReadHandler,
void(error_code, std::size_t)> init{handler};
if(in_.fc)
if(in_->fc)
{
error_code ec;
if(in_.fc->fail(ec))
return in_.ios.post(bind_handler(
if(in_->fc->fail(ec))
return in_->ios.post(bind_handler(
init.completion_handler, ec, 0));
}
{
std::unique_lock<std::mutex> lock{in_.m};
BOOST_ASSERT(! in_.op);
std::unique_lock<std::mutex> lock{in_->m};
BOOST_ASSERT(! in_->op);
if(buffer_size(buffers) == 0 ||
buffer_size(in_.b.data()) > 0)
buffer_size(in_->b.data()) > 0)
{
auto const bytes_transferred = buffer_copy(
buffers, in_.b.data(), in_.read_max);
in_.b.consume(bytes_transferred);
buffers, in_->b.data(), in_->read_max);
in_->b.consume(bytes_transferred);
lock.unlock();
++in_.nread;
in_.ios.post(bind_handler(init.completion_handler,
++in_->nread;
in_->ios.post(bind_handler(init.completion_handler,
error_code{}, bytes_transferred));
}
else if(in_.code != status::ok)
else if(in_->code != status::ok)
{
lock.unlock();
++in_.nread;
++in_->nread;
error_code ec;
if(in_.code == status::eof)
if(in_->code == status::eof)
ec = boost::asio::error::eof;
else if(in_.code == status::reset)
else if(in_->code == status::reset)
ec = boost::asio::error::connection_reset;
in_.ios.post(bind_handler(
in_->ios.post(bind_handler(
init.completion_handler, ec, 0));
}
else
{
in_.op.reset(new
in_->op.reset(new
detail::stream_impl::read_op_impl<handler_type<
ReadHandler, void(error_code, std::size_t)>,
MutableBufferSequence>{in_, buffers,
MutableBufferSequence>{*in_, buffers,
init.completion_handler});
}
}
@@ -600,7 +610,7 @@ write_some(ConstBufferSequence const& buffers)
static_assert(is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
BOOST_ASSERT(out_.code == status::ok);
BOOST_ASSERT(out_->code == status::ok);
error_code ec;
auto const bytes_transferred =
write_some(buffers, ec);
@@ -620,18 +630,18 @@ write_some(
"ConstBufferSequence requirements not met");
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
BOOST_ASSERT(out_.code == status::ok);
if(in_.fc && in_.fc->fail(ec))
BOOST_ASSERT(out_->code == status::ok);
if(in_->fc && in_->fc->fail(ec))
return 0;
auto const n = (std::min)(
buffer_size(buffers), out_.write_max);
std::unique_lock<std::mutex> lock{out_.m};
buffer_size(buffers), out_->write_max);
std::unique_lock<std::mutex> lock{out_->m};
auto const bytes_transferred =
buffer_copy(out_.b.prepare(n), buffers);
out_.b.commit(bytes_transferred);
out_.on_write();
buffer_copy(out_->b.prepare(n), buffers);
out_->b.commit(bytes_transferred);
out_->on_write();
lock.unlock();
++out_.nwrite;
++out_->nwrite;
ec.assign(0, ec.category());
return bytes_transferred;
}
@@ -648,26 +658,26 @@ async_write_some(ConstBufferSequence const& buffers,
"ConstBufferSequence requirements not met");
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
BOOST_ASSERT(out_.code == status::ok);
BOOST_ASSERT(out_->code == status::ok);
async_completion<WriteHandler,
void(error_code, std::size_t)> init{handler};
if(in_.fc)
if(in_->fc)
{
error_code ec;
if(in_.fc->fail(ec))
return in_.ios.post(bind_handler(
if(in_->fc->fail(ec))
return in_->ios.post(bind_handler(
init.completion_handler, ec, 0));
}
auto const n =
(std::min)(buffer_size(buffers), out_.write_max);
std::unique_lock<std::mutex> lock{out_.m};
(std::min)(buffer_size(buffers), out_->write_max);
std::unique_lock<std::mutex> lock{out_->m};
auto const bytes_transferred =
buffer_copy(out_.b.prepare(n), buffers);
out_.b.commit(bytes_transferred);
out_.on_write();
buffer_copy(out_->b.prepare(n), buffers);
out_->b.commit(bytes_transferred);
out_->on_write();
lock.unlock();
++out_.nwrite;
in_.ios.post(bind_handler(init.completion_handler,
++out_->nwrite;
in_->ios.post(bind_handler(init.completion_handler,
error_code{}, bytes_transferred));
return init.result.get();
}
@@ -677,9 +687,9 @@ void
teardown(websocket::role_type,
stream& s, boost::system::error_code& ec)
{
if(s.in_.fc)
if(s.in_->fc)
{
if(s.in_.fc->fail(ec))
if(s.in_->fc->fail(ec))
return;
}
else
@@ -696,7 +706,7 @@ async_teardown(websocket::role_type,
stream& s, TeardownHandler&& handler)
{
error_code ec;
if(s.in_.fc && s.in_.fc->fail(ec))
if(s.in_->fc && s.in_->fc->fail(ec))
return s.get_io_service().post(
bind_handler(std::move(handler), ec));
s.close();
@@ -709,12 +719,12 @@ void
stream::
close()
{
BOOST_ASSERT(! in_.op);
std::lock_guard<std::mutex> lock{out_.m};
if(out_.code == status::ok)
BOOST_ASSERT(! in_->op);
std::lock_guard<std::mutex> lock{out_->m};
if(out_->code == status::ok)
{
out_.code = status::eof;
out_.on_write();
out_->code = status::eof;
out_->on_write();
}
}
@@ -1,177 +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_TEST_STRING_IOSTREAM_HPP
#define BOOST_BEAST_TEST_STRING_IOSTREAM_HPP
#include <boost/beast/core/async_result.hpp>
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/buffer_prefix.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/beast/websocket/teardown.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/throw_exception.hpp>
#include <string>
namespace boost {
namespace beast {
namespace test {
/** A SyncStream and AsyncStream that reads from a string and writes to another string.
This class behaves like a socket, except that written data is
appended to a string exposed as a public data member, and when
data is read it comes from a string provided at construction.
*/
class string_iostream
{
std::string s_;
boost::asio::const_buffer cb_;
boost::asio::io_service& ios_;
std::size_t read_max_;
public:
std::string str;
string_iostream(boost::asio::io_service& ios,
std::string s, std::size_t read_max =
(std::numeric_limits<std::size_t>::max)())
: s_(std::move(s))
, cb_(boost::asio::buffer(s_))
, ios_(ios)
, read_max_(read_max)
{
}
boost::asio::io_service&
get_io_service()
{
return ios_;
}
template<class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers)
{
error_code ec;
auto const n = read_some(buffers, ec);
if(ec)
BOOST_THROW_EXCEPTION(system_error{ec});
return n;
}
template<class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers,
error_code& ec)
{
auto const n = boost::asio::buffer_copy(
buffers, buffer_prefix(read_max_, cb_));
if(n > 0)
{
ec.assign(0, ec.category());
cb_ = cb_ + n;
}
else
{
ec = boost::asio::error::eof;
}
return n;
}
template<class MutableBufferSequence, class ReadHandler>
async_return_type<
ReadHandler, void(error_code, std::size_t)>
async_read_some(MutableBufferSequence const& buffers,
ReadHandler&& handler)
{
auto const n = boost::asio::buffer_copy(
buffers, boost::asio::buffer(s_));
error_code ec;
if(n > 0)
s_.erase(0, n);
else
ec = boost::asio::error::eof;
async_completion<ReadHandler,
void(error_code, std::size_t)> init{handler};
ios_.post(bind_handler(
init.completion_handler, ec, n));
return init.result.get();
}
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers)
{
error_code ec;
auto const n = write_some(buffers, ec);
if(ec)
BOOST_THROW_EXCEPTION(system_error{ec});
return n;
}
template<class ConstBufferSequence>
std::size_t
write_some(
ConstBufferSequence const& buffers, error_code& ec)
{
ec.assign(0, ec.category());
using boost::asio::buffer_size;
using boost::asio::buffer_cast;
auto const n = buffer_size(buffers);
str.reserve(str.size() + n);
for(boost::asio::const_buffer buffer : buffers)
str.append(buffer_cast<char const*>(buffer),
buffer_size(buffer));
return n;
}
template<class ConstBufferSequence, class WriteHandler>
async_return_type<
WriteHandler, void(error_code, std::size_t)>
async_write_some(ConstBufferSequence const& buffers,
WriteHandler&& handler)
{
error_code ec;
auto const bytes_transferred = write_some(buffers, ec);
async_completion<WriteHandler,
void(error_code, std::size_t)> init{handler};
get_io_service().post(
bind_handler(init.completion_handler, ec, bytes_transferred));
return init.result.get();
}
friend
void
teardown(websocket::role_type,
string_iostream&,
boost::system::error_code& ec)
{
ec.assign(0, ec.category());
}
template<class TeardownHandler>
friend
void
async_teardown(websocket::role_type,
string_iostream& stream,
TeardownHandler&& handler)
{
stream.get_io_service().post(
bind_handler(std::move(handler),
error_code{}));
}
};
} // test
} // beast
} // boost
#endif
@@ -1,165 +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_TEST_STRING_ISTREAM_HPP
#define BOOST_BEAST_TEST_STRING_ISTREAM_HPP
#include <boost/beast/core/async_result.hpp>
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/beast/websocket/teardown.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/throw_exception.hpp>
#include <string>
namespace boost {
namespace beast {
namespace test {
/** A SyncStream and AsyncStream that reads from a string.
This class behaves like a socket, except that written data is simply
discarded, and when data is read it comes from a string provided
at construction.
*/
class string_istream
{
std::string s_;
boost::asio::const_buffer cb_;
boost::asio::io_service& ios_;
std::size_t read_max_;
public:
string_istream(boost::asio::io_service& ios,
std::string s, std::size_t read_max =
(std::numeric_limits<std::size_t>::max)())
: s_(std::move(s))
, cb_(boost::asio::buffer(s_))
, ios_(ios)
, read_max_(read_max)
{
}
boost::asio::io_service&
get_io_service()
{
return ios_;
}
template<class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers)
{
error_code ec;
auto const n = read_some(buffers, ec);
if(ec)
BOOST_THROW_EXCEPTION(system_error{ec});
return n;
}
template<class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers,
error_code& ec)
{
auto const n = boost::asio::buffer_copy(
buffers, cb_, read_max_);
if(n > 0)
{
ec.assign(0, ec.category());
cb_ = cb_ + n;
}
else
{
ec = boost::asio::error::eof;
}
return n;
}
template<class MutableBufferSequence, class ReadHandler>
async_return_type<
ReadHandler, void(error_code, std::size_t)>
async_read_some(MutableBufferSequence const& buffers,
ReadHandler&& handler)
{
auto const n = boost::asio::buffer_copy(
buffers, boost::asio::buffer(s_));
error_code ec;
if(n > 0)
s_.erase(0, n);
else
ec = boost::asio::error::eof;
async_completion<ReadHandler,
void(error_code, std::size_t)> init{handler};
ios_.post(bind_handler(
init.completion_handler, ec, n));
return init.result.get();
}
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers)
{
error_code ec;
auto const n = write_some(buffers, ec);
if(ec)
BOOST_THROW_EXCEPTION(system_error{ec});
return n;
}
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers,
error_code& ec)
{
ec.assign(0, ec.category());
return boost::asio::buffer_size(buffers);
}
template<class ConstBuffeSequence, class WriteHandler>
async_return_type<
WriteHandler, void(error_code, std::size_t)>
async_write_some(ConstBuffeSequence const& buffers,
WriteHandler&& handler)
{
async_completion<WriteHandler,
void(error_code, std::size_t)> init{handler};
ios_.post(bind_handler(init.completion_handler,
error_code{}, boost::asio::buffer_size(buffers)));
return init.result.get();
}
friend
void
teardown(websocket::role_type,
string_istream&,
boost::system::error_code& ec)
{
ec.assign(0, ec.category());
}
template<class TeardownHandler>
friend
void
async_teardown(websocket::role_type,
string_istream& stream,
TeardownHandler&& handler)
{
stream.get_io_service().post(
bind_handler(std::move(handler),
error_code{}));
}
};
} // test
} // beast
} // boost
#endif
@@ -1,153 +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_TEST_STRING_OSTREAM_HPP
#define BOOST_BEAST_TEST_STRING_OSTREAM_HPP
#include <boost/beast/core/async_result.hpp>
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/buffer_prefix.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/beast/websocket/teardown.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/throw_exception.hpp>
#include <string>
namespace boost {
namespace beast {
namespace test {
class string_ostream
{
boost::asio::io_service& ios_;
std::size_t write_max_;
public:
std::string str;
explicit
string_ostream(boost::asio::io_service& ios,
std::size_t write_max =
(std::numeric_limits<std::size_t>::max)())
: ios_(ios)
, write_max_(write_max)
{
}
boost::asio::io_service&
get_io_service()
{
return ios_;
}
template<class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers)
{
error_code ec;
auto const n = read_some(buffers, ec);
if(ec)
BOOST_THROW_EXCEPTION(system_error{ec});
return n;
}
template<class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const&,
error_code& ec)
{
ec = boost::asio::error::eof;
return 0;
}
template<class MutableBufferSequence, class ReadHandler>
async_return_type<
ReadHandler, void(error_code, std::size_t)>
async_read_some(MutableBufferSequence const&,
ReadHandler&& handler)
{
async_completion<ReadHandler,
void(error_code, std::size_t)> init{handler};
ios_.post(bind_handler(init.completion_handler,
boost::asio::error::eof, 0));
return init.result.get();
}
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers)
{
error_code ec;
auto const n = write_some(buffers, ec);
if(ec)
BOOST_THROW_EXCEPTION(system_error{ec});
return n;
}
template<class ConstBufferSequence>
std::size_t
write_some(
ConstBufferSequence const& buffers, error_code& ec)
{
ec.assign(0, ec.category());
using boost::asio::buffer_size;
using boost::asio::buffer_cast;
auto const n =
(std::min)(buffer_size(buffers), write_max_);
str.reserve(str.size() + n);
for(boost::asio::const_buffer buffer :
buffer_prefix(n, buffers))
str.append(buffer_cast<char const*>(buffer),
buffer_size(buffer));
return n;
}
template<class ConstBufferSequence, class WriteHandler>
async_return_type<
WriteHandler, void(error_code, std::size_t)>
async_write_some(ConstBufferSequence const& buffers,
WriteHandler&& handler)
{
error_code ec;
auto const bytes_transferred = write_some(buffers, ec);
async_completion<WriteHandler,
void(error_code, std::size_t)> init{handler};
get_io_service().post(
bind_handler(init.completion_handler, ec, bytes_transferred));
return init.result.get();
}
friend
void
teardown(websocket::role_type,
string_ostream&,
boost::system::error_code& ec)
{
ec.assign(0, ec.category());
}
template<class TeardownHandler>
friend
void
async_teardown(websocket::role_type,
string_ostream& stream,
TeardownHandler&& handler)
{
stream.get_io_service().post(
bind_handler(std::move(handler),
error_code{}));
}
};
} // test
} // beast
} // boost
#endif