Add buffers_generator

This commit is contained in:
sehe
2022-05-20 09:54:04 -07:00
committed by Vinnie Falco
parent 344c10ff1e
commit 84e689c447
11 changed files with 871 additions and 3 deletions

View File

@ -0,0 +1,140 @@
[/
Copyright (c) 2016-2022 Vinnie Falco (vinnie dot falco at gmail dot com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Official repository: https://github.com/boostorg/beast
]
[section:BuffersGenerator BuffersGenerator]
A [*BuffersGenerator] provides a generalized interface for generating
serialized data for sequential processing.
The generator will be asked to produce buffers. The consuming code will signal
how much of the data has been consumed, and repeatedly query for buffers until
no more data is available, or the generator indicates an error condition.
Overloads of [link beast.ref.boost__beast__write `write`] and [link
beast.ref.boost__beast__async_write `async_write`] operations are provided as
free functions. These operations will consume the output of a
[*BuffersGenerator] and process the data by writing them to a
__SyncWriteStream__ or __AsyncWriteStream__ respectively.
[heading Associated Types]
* [link beast.ref.boost__beast__is_buffers_generator `is_buffers_generator`]
* __ConstBufferSequence__
[heading Requirements]
In this table:
* `G` denotes a type meeting the requirements of [*BuffersGenerator].
* `g` denotes a value of type `G`.
* `n` is a value of type `std::size_t`.
* `ec` is a value of type [link beast.ref.boost__beast__error_code `error_code&`].
[table Valid expressions
[[Expression] [Type] [Semantics, Pre/Post-conditions]]
[
[`G::const_buffers_type`]
[]
[
A type which meets the requirements of __ConstBufferSequence__.
This is the type of buffer returned by `g.prepare(ec)`.
]
][
[`g.is_done()`]
[`bool`]
[
Called to ask the generator for its completion status.
A generator has completed when no new buffer will be produced and
previously produced buffers have been fully consumed.
[*Note:] The result of invoking `prepare` on `g` once it has completed
is unspecified.
This member function does not alter the state of the generator.
]
][
[`g.prepare(ec)`]
[`G::const_buffers_type`]
[
Called to ask the generator to produce buffers containing data for
processing.
The returned value is the __ConstBufferSequence__ representing
unconsumed data.
The function will ensure that `ec.failed()` is `false` if there was no
error or set to the appropriate error code if there was one.
If no unconsumed data is available, this operation shall make progress
to eventually reach completion.
The result of invoking `prepare` after completion or encountered
error(s) is defined by the generator implementation. It can not be
assumed to be meaningful or safe to do so, in general.
The capacity of the buffer returned is defined by the generator
implementation.
[*Note:] Any buffers obtained by previous calls to `prepare` are
invalidated.
]
][
[`g.consume(n)`]
[]
[
This function is called to signal that the consumer (caller) of the
generator has processed part of the data returned by the previous call
to `prepare`.
The value of `n` indicates how much of the data processed (in bytes).
When `n` exceeds the number of bytes returned from the last call to
`prepare`, `consume` shall behave as if `n` was equal to that number.
Remaining unconsumed data will be returned from subsequent calls to
`prepare`.
[*Note:] Any buffers obtained by previous calls to `prepare` are
invalidated.
]
][
[`is_buffers_generator<G>`]
[`std::true_type`]
[
An alias for `std::true_type` for `G`, otherwise an alias
for `std::false_type`.
]
]]
[heading Exemplar]
```
// A buffer sequence generator
struct BuffersGenerator
{
using const_buffers_type = net::const_buffer;
bool is_done();
const_buffers_type prepare( error_code& ec );
void consume( std::size_t n );
};
static_assert(
is_buffers_generator<BuffersGenerator>::value, "");
```
[heading Models]
[heading Algorithms]
* [link beast.ref.boost__beast__async_write `async_write`]
* [link beast.ref.boost__beast__write `write`]
[endsect]

View File

@ -15,6 +15,7 @@ This section describes all of the concepts defined by the library.
[include BodyReader.qbk]
[include BodyWriter.qbk]
[include BufferSequence.qbk]
[include BuffersGenerator.qbk]
[include DynamicBuffer.qbk]
[include Fields.qbk]
[include FieldsWriter.qbk]

View File

@ -95,6 +95,7 @@
[def __BodyReader__ [link beast.concepts.BodyReader ['BodyReader]]]
[def __BodyWriter__ [link beast.concepts.BodyWriter ['BodyWriter]]]
[def __BufferSequence__ [link beast.concepts.BufferSequence ['BufferSequence]]]
[def __BuffersGenerator__ [link beast.concepts.BufferSequence ['BuffersGenerator]]]
[def __DynamicBuffer__ [link beast.concepts.DynamicBuffer ['DynamicBuffer]]]
[def __Fields__ [link beast.concepts.Fields ['Fields]]]
[def __FieldsWriter__ [link beast.concepts.FieldsWriter ['FieldsWriter]]]

View File

@ -76,15 +76,15 @@
<bridgehead renderas="sect3">Type Traits</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.boost__beast__executor_type">executor_type</link></member>
<member><link linkend="beast.ref.boost__beast__lowest_layer_type">lowest_layer_type</link></member>
<member><link linkend="beast.ref.boost__beast__has_get_executor">has_get_executor</link></member>
<member><link linkend="beast.ref.boost__beast__is_async_read_stream">is_async_read_stream</link></member>
<member><link linkend="beast.ref.boost__beast__is_async_write_stream">is_async_write_stream</link></member>
<member><link linkend="beast.ref.boost__beast__is_async_stream">is_async_stream</link></member>
<member><link linkend="beast.ref.boost__beast__is_async_write_stream">is_async_write_stream</link></member>
<member><link linkend="beast.ref.boost__beast__is_file">is_file</link></member>
<member><link linkend="beast.ref.boost__beast__is_sync_read_stream">is_sync_read_stream</link></member>
<member><link linkend="beast.ref.boost__beast__is_sync_stream">is_sync_stream</link></member>
<member><link linkend="beast.ref.boost__beast__is_sync_write_stream">is_sync_write_stream</link></member>
<member><link linkend="beast.ref.boost__beast__lowest_layer_type">lowest_layer_type</link></member>
</simplelist>
<bridgehead renderas="sect3">SSL</bridgehead>
<simplelist type="vert" columns="1">
@ -141,6 +141,7 @@
</entry><entry valign="top">
<bridgehead renderas="sect3">Functions</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.boost__beast__async_write">async_write</link></member>
<member><link linkend="beast.ref.boost__beast__buffer_bytes">buffer_bytes</link></member>
<member><link linkend="beast.ref.boost__beast__buffers_cat">buffers_cat</link></member>
<member><link linkend="beast.ref.boost__beast__buffers_front">buffers_front</link></member>
@ -152,12 +153,14 @@
<member><link linkend="beast.ref.boost__beast__ostream">ostream</link></member>
<member><link linkend="beast.ref.boost__beast__read_size">read_size</link></member>
<member><link linkend="beast.ref.boost__beast__read_size_or_throw">read_size_or_throw</link></member>
<member><link linkend="beast.ref.boost__beast__write">write</link></member>
</simplelist>
</entry><entry valign="top">
<bridgehead renderas="sect3">Type Traits</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.boost__beast__buffers_type">buffers_type</link></member>
<member><link linkend="beast.ref.boost__beast__buffers_iterator_type">buffers_iterator_type</link></member>
<member><link linkend="beast.ref.boost__beast__buffers_type">buffers_type</link></member>
<member><link linkend="beast.ref.boost__beast__is_buffers_generator">is_buffers_generator</link></member>
<member><link linkend="beast.ref.boost__beast__is_const_buffer_sequence">is_const_buffer_sequence</link></member>
<member><link linkend="beast.ref.boost__beast__is_mutable_buffer_sequence">is_mutable_buffer_sequence</link></member>
</simplelist>
@ -165,6 +168,7 @@
<bridgehead renderas="sect3">Concepts</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.concepts.BufferSequence">BufferSequence</link></member>
<member><link linkend="beast.concepts.BuffersGenerator">BuffersGenerator</link></member>
<member><link linkend="beast.concepts.DynamicBuffer">DynamicBuffer</link></member>
</simplelist>
</entry>

View File

@ -23,6 +23,7 @@
'Body',
'BufferSequence',
'BufferSequence', (: TODO: Was this intended to be 'BufferSequence_' ?? :)
'BuffersGenerator',
'CompletionCondition',
'CompletionHandler',
'CompletionToken',

View File

@ -19,6 +19,7 @@
#include <boost/beast/core/buffered_read_stream.hpp>
#include <boost/beast/core/buffers_adaptor.hpp>
#include <boost/beast/core/buffers_cat.hpp>
#include <boost/beast/core/buffers_generator.hpp>
#include <boost/beast/core/buffers_prefix.hpp>
#include <boost/beast/core/buffers_range.hpp>
#include <boost/beast/core/buffers_suffix.hpp>

View File

@ -0,0 +1,206 @@
//
// Copyright (c) 2022 Seth Heeren (sgheeren 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_BUFFERS_GENERATOR_HPP
#define BOOST_BEAST_CORE_BUFFERS_GENERATOR_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/detail/type_traits.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/asio/async_result.hpp>
#include <type_traits>
namespace boost {
namespace beast {
/** Determine if type satisfies the <em>BuffersGenerator</em> requirements.
This metafunction is used to determine if the specified type meets the
requirements for a buffers generator.
The static member `value` will evaluate to `true` if so, `false` otherwise.
@tparam T a type to check
*/
#ifdef BOOST_BEAST_DOXYGEN
template <class T>
struct is_buffers_generator
: integral_constant<bool, automatically_determined>
{
};
#else
template<class T, class = void>
struct is_buffers_generator
: std::false_type
{
};
template<class T>
struct is_buffers_generator<
T, detail::void_t<decltype(
bool(std::declval<T const&>().is_done()),
typename T::const_buffers_type(
std::declval<T&>().prepare(
std::declval<error_code&>())),
std::declval<T&>().consume(
std::size_t{})
)>> : std::true_type
{
};
#endif
/** Write all output from a BuffersGenerator to a stream.
This function is used to write all of the buffers generated
by a caller-provided BuffersGenerator to a stream. The call
will block until one of the following conditions is true:
@li The generator returns an empty buffers sequence.
@li An error occurs.
This operation is implemented in terms of one or more calls
to the stream's `write_some` function.
@param stream The stream to which the data is to be written.
The type must support the <em>SyncWriteStream</em> concept.
@param generator The generator to use.
@param ec Set to the error, if any occurred.
@return The number of bytes written to the stream.
@see BuffersGenerator
*/
template<
class SyncWriteStream,
class BuffersGenerator
#if ! BOOST_BEAST_DOXYGEN
,
typename std::enable_if<is_buffers_generator<
typename std::decay<BuffersGenerator>::
type>::value>::type* = nullptr
#endif
>
std::size_t
write(
SyncWriteStream& stream,
BuffersGenerator&& generator,
beast::error_code& ec);
/** Write all output from a BuffersGenerator to a stream.
This function is used to write all of the buffers generated
by a caller-provided BuffersGenerator to a stream. The call
will block until one of the following conditions is true:
@li The generator returns an empty buffers sequence.
@li An error occurs.
This operation is implemented in terms of one or more calls
to the stream's `write_some` function.
@param stream The stream to which the data is to be written.
The type must support the <em>SyncWriteStream</em> concept.
@param generator The generator to use.
@return The number of bytes written to the stream.
@throws system_error Thrown on failure.
@see BuffersGenerator
*/
template<
class SyncWriteStream,
class BuffersGenerator
#if ! BOOST_BEAST_DOXYGEN
,
typename std::enable_if<is_buffers_generator<
typename std::decay<BuffersGenerator>::
type>::value>::type* = nullptr
#endif
>
std::size_t
write(
SyncWriteStream& stream,
BuffersGenerator&& generator);
/** Write all output from a BuffersGenerator asynchronously to a
stream.
This function is used to write all of the buffers generated
by a caller-provided @ref BuffersGenerator to a stream. The
function call always returns immediately. The asynchronous
operation will continue until one of the following
conditions is true:
@li The generator returns an empty buffers sequence.
@li An error occurs.
This operation is implemented in terms of zero or more calls
to the stream's `async_write_some` function, and is known as
a <em>composed operation</em>. The program must ensure that
the stream performs no other writes until this operation
completes.
@param stream The stream to which the data is to be written.
The type must support the <em>SyncWriteStream</em> concept.
@param generator The generator to use.
@param handler The completion handler to invoke when the
operation completes. The implementation takes ownership of
the handler by performing a decay-copy. The equivalent
function signature of the handler must be:
@code
void handler(
error_code const& error, // result of operation
std::size_t bytes_transferred // the number of bytes written to the stream
);
@endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from
within this function. Invocation of the handler will be
performed in a manner equivalent to using `net::post`.
@see BuffersGenerator
*/
template<
class AsyncWriteStream,
class BuffersGenerator,
class CompletionToken
#if !BOOST_BEAST_DOXYGEN
,
typename std::enable_if<is_buffers_generator<
BuffersGenerator>::value>::type* = nullptr
#endif
>
auto
async_write(
AsyncWriteStream& stream,
BuffersGenerator generator,
CompletionToken&& token) //
-> typename net::async_result<
typename std::decay<
CompletionToken>::type,
void(error_code, std::size_t)>::
return_type;
} // namespace beast
} // namespace boost
#include <include/boost/beast/core/impl/buffers_generator.hpp>
#endif

View File

@ -0,0 +1,176 @@
//
// Copyright (c) 2022 Seth Heeren (sgheeren 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_BUFFERS_GENERATOR_HPP
#define BOOST_BEAST_CORE_IMPL_BUFFERS_GENERATOR_HPP
#include <boost/beast/core/buffers_generator.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/compose.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/beast/core/buffer_traits.hpp>
#include <boost/beast/core/stream_traits.hpp>
#include <boost/throw_exception.hpp>
#include <type_traits>
namespace boost {
namespace beast {
namespace detail {
template <
class AsyncWriteStream,
class BuffersGenerator>
struct write_buffers_generator_op
: boost::asio::coroutine
{
write_buffers_generator_op(
AsyncWriteStream& s, BuffersGenerator g)
: s_(s)
, g_(std::move(g))
{
}
template<class Self>
void operator()(
Self& self, error_code ec = {}, std::size_t n = 0)
{
BOOST_ASIO_CORO_REENTER(*this)
{
while(! g_.is_done())
{
BOOST_ASIO_CORO_YIELD
{
auto cb = g_.prepare(ec);
if(ec)
goto complete;
s_.async_write_some(
cb, std::move(self));
}
if(ec)
goto complete;
g_.consume(n);
total_ += n;
}
complete:
self.complete(ec, total_);
}
}
private:
AsyncWriteStream& s_;
BuffersGenerator g_;
std::size_t total_ = 0;
};
} // detail
template<
class SyncWriteStream,
class BuffersGenerator,
typename std::enable_if< //
is_buffers_generator<typename std::decay<
BuffersGenerator>::type>::value>::type* /*= nullptr*/
>
size_t
write(
SyncWriteStream& stream,
BuffersGenerator&& generator,
beast::error_code& ec)
{
static_assert(
is_sync_write_stream<SyncWriteStream>::value,
"SyncWriteStream type requirements not met");
ec.clear();
size_t total = 0;
while(! generator.is_done())
{
auto cb = generator.prepare(ec);
if(ec)
break;
size_t n = net::write(stream, cb, ec);
if(ec)
break;
generator.consume(n);
total += n;
}
return total;
}
//----------------------------------------------------------
template<
class SyncWriteStream,
class BuffersGenerator,
typename std::enable_if<is_buffers_generator<
typename std::decay<BuffersGenerator>::type>::value>::
type* /*= nullptr*/
>
std::size_t
write(SyncWriteStream& stream, BuffersGenerator&& generator)
{
static_assert(
is_sync_write_stream<SyncWriteStream>::value,
"SyncWriteStream type requirements not met");
beast::error_code ec;
std::size_t n = write(
stream, std::forward<BuffersGenerator>(generator), ec);
if(ec)
BOOST_THROW_EXCEPTION(system_error{ ec });
return n;
}
//----------------------------------------------------------
template<
class AsyncWriteStream,
class BuffersGenerator,
class CompletionToken,
typename std::enable_if<is_buffers_generator<
BuffersGenerator>::value>::type* /*= nullptr*/
>
auto
async_write(
AsyncWriteStream& stream,
BuffersGenerator generator,
CompletionToken&& token) ->
typename net::async_result<
typename std::decay<CompletionToken>::type,
void(error_code, std::size_t)>::return_type
{
static_assert(
beast::is_async_write_stream<AsyncWriteStream>::value,
"AsyncWriteStream type requirements not met");
return net::async_compose< //
CompletionToken,
void(error_code, std::size_t)>(
detail::write_buffers_generator_op<
AsyncWriteStream,
BuffersGenerator>{ stream, std::move(generator) },
token,
stream);
}
} // namespace beast
} // namespace boost
#endif

View File

@ -35,6 +35,7 @@ add_executable (tests-beast-core
buffered_read_stream.cpp
buffers_adaptor.cpp
buffers_cat.cpp
buffers_generator.cpp
buffers_prefix.cpp
buffers_range.cpp
buffers_suffix.cpp

View File

@ -26,6 +26,7 @@ local SOURCES =
buffered_read_stream.cpp
buffers_adaptor.cpp
buffers_cat.cpp
buffers_generator.cpp
buffers_prefix.cpp
buffers_range.cpp
buffers_suffix.cpp

View File

@ -0,0 +1,336 @@
//
// Copyright (c) 2022 Seth Heeren (sgheeren at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
// Test that header file is self-contained.
#include <boost/beast/core/buffers_generator.hpp>
#include "test_buffer.hpp"
#include <boost/asio/error.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp>
#include <boost/beast/core/buffers_suffix.hpp>
#include <boost/beast/core/detail/static_ostream.hpp>
#include <boost/beast/core/flat_buffer.hpp>
#include <boost/beast/_experimental/test/stream.hpp>
#include <iostream>
#include <numeric>
namespace boost {
namespace beast {
namespace detail {
struct test_buffers_generator {
using underlying_buffer_sequence = std::array<net::const_buffer, 2>;
using const_buffers_type = buffers_suffix<underlying_buffer_sequence>;
std::size_t iterations_ = 5;
bool verbose_ = false;
error_code emulate_error_;
test_buffers_generator(
error_code emulate_error = {}, bool verbose = false)
: verbose_(verbose)
, emulate_error_(emulate_error)
{
}
const_buffers_type cur_{};
bool
is_done() const
{
return iterations_ == 0 && ! buffer_bytes(cur_);
}
const_buffers_type
prepare(error_code& ec)
{
ec = {};
BEAST_EXPECT(! is_done());
if(verbose_)
std::clog
<< "prepare, iterations_:" << iterations_
<< " '" << buffers_to_string(cur_) << "' ";
if (!buffer_bytes(cur_)) {
if (iterations_) {
cur_ = const_buffers_type(
underlying_buffer_sequence{
net::buffer("abcde", iterations_),
net::buffer("12345", iterations_),
});
iterations_ -= 1;
}
if(emulate_error_ && iterations_ == 3)
{
ec = emulate_error_; // generate the specified error
}
}
if (verbose_)
std::clog << " -> '" << buffers_to_string(cur_)
<< "'\n";
return const_buffers_type{ cur_ };
}
void consume(std::size_t n) {
cur_.consume(n);
}
};
} // namespace detail
class buffers_generator_test : public unit_test::suite
{
public:
void
testMinimalGenerator(error_code emulate_error)
{
static_assert(
is_buffers_generator<
detail::test_buffers_generator>::value,
"buffers_generator not modeled");
detail::test_buffers_generator gen{emulate_error};
error_code ec;
std::vector<std::string> actual;
while(! gen.is_done())
{
detail::test_buffers_generator::const_buffers_type b =
gen.prepare(ec);
if (ec) {
BEAST_EXPECT(emulate_error == ec);
// In test we ignore the error because we know that's okay.
//
// For general models of BuffersGenerator behaviour is
// unspecified when using a generator after receiving an error.
}
actual.push_back(buffers_to_string(b));
gen.consume(3); // okay if > buffer_bytes
}
BEAST_EXPECT(! ec.failed());
if(! emulate_error)
{
BEAST_EXPECT(
(actual ==
std::vector<std::string>{
"abcde12345", "de12345", "2345", "5",
"abcd1234", "d1234", "34", "abc123", "123",
"ab12", "2", "a1" }));
}
}
void
testWrite(error_code emulate_error)
{
net::io_context ioc;
test::stream out(ioc), in(ioc);
test::connect(out, in);
{
detail::test_buffers_generator gen{emulate_error};
beast::error_code ec;
auto total = write(out, gen, ec);
BEAST_EXPECT(ec == emulate_error);
if(! emulate_error)
{
BEAST_EXPECT(total == 30);
BEAST_EXPECT(5 == out.nwrite());
BEAST_EXPECT(30 == in.nwrite_bytes());
BEAST_EXPECT(
"abcde12345abcd1234abc123ab12a1" == in.str());
} else
{
BEAST_EXPECT(total == 10);
BEAST_EXPECT(1 == out.nwrite());
BEAST_EXPECT(10 == in.nwrite_bytes());
BEAST_EXPECT("abcde12345" == in.str());
}
}
in.clear();
{
error_code ec;
auto total = write(out, detail::test_buffers_generator{emulate_error}, ec);
BEAST_EXPECT(ec == emulate_error);
if(! emulate_error)
{
BEAST_EXPECT(total == 30);
BEAST_EXPECT("abcde12345abcd1234abc123ab12a1" == in.str());
} else
{
BEAST_EXPECT(total == 10);
BEAST_EXPECT("abcde12345" == in.str());
}
}
}
void
testWriteException(error_code emulate_error)
{
net::io_context ioc;
test::stream out(ioc), in(ioc);
{
test::connect(out, in);
detail::test_buffers_generator gen{emulate_error};
try {
auto total = write(out, gen);
if (emulate_error)
BEAST_EXPECT(!"unreachable");
BEAST_EXPECT(total == 30);
} catch(system_error const& se)
{
BEAST_EXPECT(se.code() == emulate_error);
}
}
if(! emulate_error)
{
BEAST_EXPECT(5 == out.nwrite());
BEAST_EXPECT(30 == in.nwrite_bytes());
BEAST_EXPECT(
"abcde12345abcd1234abc123ab12a1" == in.str());
} else
{
BEAST_EXPECT(1 == out.nwrite());
BEAST_EXPECT(10 == in.nwrite_bytes());
BEAST_EXPECT("abcde12345" == in.str());
}
}
void
testAsyncWrite(error_code emulate_error)
{
net::io_context ioc;
test::stream out(ioc), in(ioc);
{
test::connect(out, in);
detail::test_buffers_generator gen{emulate_error};
async_write(
out,
gen,
[&](error_code ec, std::size_t total)
{
BEAST_EXPECT(ec == emulate_error);
if(! emulate_error)
{
BEAST_EXPECT(total == 30);
} else
{
BEAST_EXPECT(total == 10);
}
});
ioc.run();
}
if(! emulate_error)
{
BEAST_EXPECT(5 == out.nwrite());
BEAST_EXPECT(30 == in.nwrite_bytes());
BEAST_EXPECT(
"abcde12345abcd1234abc123ab12a1" == in.str());
} else
{
BEAST_EXPECT(1 == out.nwrite());
BEAST_EXPECT(10 == in.nwrite_bytes());
BEAST_EXPECT("abcde12345" == in.str());
}
}
void
testWriteFail()
{
net::io_context ioc;
test::fail_count fc { 3 };
test::stream out(ioc, fc), in(ioc);
{
test::connect(out, in);
detail::test_buffers_generator gen;
try {
/*auto total =*/ write(out, gen);
BEAST_EXPECT(! "unreachable");
} catch(system_error const& se)
{
BEAST_EXPECT(se.code() == test::error::test_failure);
}
}
BEAST_EXPECT(3 == out.nwrite());
BEAST_EXPECT(18 == in.nwrite_bytes()); // first two writes 10+8
BEAST_EXPECT("abcde12345abcd1234" == in.str());
}
void
testAsyncWriteFail()
{
net::io_context ioc;
test::fail_count fc { 3 };
test::stream out(ioc, fc), in(ioc);
{
test::connect(out, in);
detail::test_buffers_generator gen;
async_write(
out, gen, [&](error_code ec, std::size_t total) {
BEAST_EXPECT(total == 18);
BEAST_EXPECT(ec == test::error::test_failure);
});
ioc.run();
}
BEAST_EXPECT(3 == out.nwrite());
BEAST_EXPECT(18 == in.nwrite_bytes()); // first two writes 10+8
BEAST_EXPECT("abcde12345abcd1234" == in.str());
}
void
run() override
{
for(error_code emulate_error :
{error_code{}, error_code{error::timeout}})
{
testMinimalGenerator(emulate_error);
testWrite(emulate_error);
testWriteException(emulate_error);
testAsyncWrite(emulate_error);
}
testWriteFail();
testAsyncWriteFail();
}
};
BEAST_DEFINE_TESTSUITE(beast,core,buffers_generator);
} // beast
} // boost