mirror of
https://github.com/boostorg/beast.git
synced 2025-07-31 21:34:46 +02:00
Add buffers_generator
This commit is contained in:
140
doc/qbk/07_concepts/BuffersGenerator.qbk
Normal file
140
doc/qbk/07_concepts/BuffersGenerator.qbk
Normal 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]
|
@@ -15,6 +15,7 @@ This section describes all of the concepts defined by the library.
|
|||||||
[include BodyReader.qbk]
|
[include BodyReader.qbk]
|
||||||
[include BodyWriter.qbk]
|
[include BodyWriter.qbk]
|
||||||
[include BufferSequence.qbk]
|
[include BufferSequence.qbk]
|
||||||
|
[include BuffersGenerator.qbk]
|
||||||
[include DynamicBuffer.qbk]
|
[include DynamicBuffer.qbk]
|
||||||
[include Fields.qbk]
|
[include Fields.qbk]
|
||||||
[include FieldsWriter.qbk]
|
[include FieldsWriter.qbk]
|
||||||
|
@@ -95,6 +95,7 @@
|
|||||||
[def __BodyReader__ [link beast.concepts.BodyReader ['BodyReader]]]
|
[def __BodyReader__ [link beast.concepts.BodyReader ['BodyReader]]]
|
||||||
[def __BodyWriter__ [link beast.concepts.BodyWriter ['BodyWriter]]]
|
[def __BodyWriter__ [link beast.concepts.BodyWriter ['BodyWriter]]]
|
||||||
[def __BufferSequence__ [link beast.concepts.BufferSequence ['BufferSequence]]]
|
[def __BufferSequence__ [link beast.concepts.BufferSequence ['BufferSequence]]]
|
||||||
|
[def __BuffersGenerator__ [link beast.concepts.BufferSequence ['BuffersGenerator]]]
|
||||||
[def __DynamicBuffer__ [link beast.concepts.DynamicBuffer ['DynamicBuffer]]]
|
[def __DynamicBuffer__ [link beast.concepts.DynamicBuffer ['DynamicBuffer]]]
|
||||||
[def __Fields__ [link beast.concepts.Fields ['Fields]]]
|
[def __Fields__ [link beast.concepts.Fields ['Fields]]]
|
||||||
[def __FieldsWriter__ [link beast.concepts.FieldsWriter ['FieldsWriter]]]
|
[def __FieldsWriter__ [link beast.concepts.FieldsWriter ['FieldsWriter]]]
|
||||||
|
@@ -76,15 +76,15 @@
|
|||||||
<bridgehead renderas="sect3">Type Traits</bridgehead>
|
<bridgehead renderas="sect3">Type Traits</bridgehead>
|
||||||
<simplelist type="vert" columns="1">
|
<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__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__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_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_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_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_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_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__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>
|
</simplelist>
|
||||||
<bridgehead renderas="sect3">SSL</bridgehead>
|
<bridgehead renderas="sect3">SSL</bridgehead>
|
||||||
<simplelist type="vert" columns="1">
|
<simplelist type="vert" columns="1">
|
||||||
@@ -141,6 +141,7 @@
|
|||||||
</entry><entry valign="top">
|
</entry><entry valign="top">
|
||||||
<bridgehead renderas="sect3">Functions</bridgehead>
|
<bridgehead renderas="sect3">Functions</bridgehead>
|
||||||
<simplelist type="vert" columns="1">
|
<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__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_cat">buffers_cat</link></member>
|
||||||
<member><link linkend="beast.ref.boost__beast__buffers_front">buffers_front</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__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">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__read_size_or_throw">read_size_or_throw</link></member>
|
||||||
|
<member><link linkend="beast.ref.boost__beast__write">write</link></member>
|
||||||
</simplelist>
|
</simplelist>
|
||||||
</entry><entry valign="top">
|
</entry><entry valign="top">
|
||||||
<bridgehead renderas="sect3">Type Traits</bridgehead>
|
<bridgehead renderas="sect3">Type Traits</bridgehead>
|
||||||
<simplelist type="vert" columns="1">
|
<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_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_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>
|
<member><link linkend="beast.ref.boost__beast__is_mutable_buffer_sequence">is_mutable_buffer_sequence</link></member>
|
||||||
</simplelist>
|
</simplelist>
|
||||||
@@ -165,6 +168,7 @@
|
|||||||
<bridgehead renderas="sect3">Concepts</bridgehead>
|
<bridgehead renderas="sect3">Concepts</bridgehead>
|
||||||
<simplelist type="vert" columns="1">
|
<simplelist type="vert" columns="1">
|
||||||
<member><link linkend="beast.concepts.BufferSequence">BufferSequence</link></member>
|
<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>
|
<member><link linkend="beast.concepts.DynamicBuffer">DynamicBuffer</link></member>
|
||||||
</simplelist>
|
</simplelist>
|
||||||
</entry>
|
</entry>
|
||||||
|
@@ -23,6 +23,7 @@
|
|||||||
'Body',
|
'Body',
|
||||||
'BufferSequence',
|
'BufferSequence',
|
||||||
'BufferSequence', (: TODO: Was this intended to be 'BufferSequence_' ?? :)
|
'BufferSequence', (: TODO: Was this intended to be 'BufferSequence_' ?? :)
|
||||||
|
'BuffersGenerator',
|
||||||
'CompletionCondition',
|
'CompletionCondition',
|
||||||
'CompletionHandler',
|
'CompletionHandler',
|
||||||
'CompletionToken',
|
'CompletionToken',
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
#include <boost/beast/core/buffered_read_stream.hpp>
|
#include <boost/beast/core/buffered_read_stream.hpp>
|
||||||
#include <boost/beast/core/buffers_adaptor.hpp>
|
#include <boost/beast/core/buffers_adaptor.hpp>
|
||||||
#include <boost/beast/core/buffers_cat.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_prefix.hpp>
|
||||||
#include <boost/beast/core/buffers_range.hpp>
|
#include <boost/beast/core/buffers_range.hpp>
|
||||||
#include <boost/beast/core/buffers_suffix.hpp>
|
#include <boost/beast/core/buffers_suffix.hpp>
|
||||||
|
206
include/boost/beast/core/buffers_generator.hpp
Normal file
206
include/boost/beast/core/buffers_generator.hpp
Normal 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
|
176
include/boost/beast/core/impl/buffers_generator.hpp
Normal file
176
include/boost/beast/core/impl/buffers_generator.hpp
Normal 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
|
@@ -35,6 +35,7 @@ add_executable (tests-beast-core
|
|||||||
buffered_read_stream.cpp
|
buffered_read_stream.cpp
|
||||||
buffers_adaptor.cpp
|
buffers_adaptor.cpp
|
||||||
buffers_cat.cpp
|
buffers_cat.cpp
|
||||||
|
buffers_generator.cpp
|
||||||
buffers_prefix.cpp
|
buffers_prefix.cpp
|
||||||
buffers_range.cpp
|
buffers_range.cpp
|
||||||
buffers_suffix.cpp
|
buffers_suffix.cpp
|
||||||
|
@@ -26,6 +26,7 @@ local SOURCES =
|
|||||||
buffered_read_stream.cpp
|
buffered_read_stream.cpp
|
||||||
buffers_adaptor.cpp
|
buffers_adaptor.cpp
|
||||||
buffers_cat.cpp
|
buffers_cat.cpp
|
||||||
|
buffers_generator.cpp
|
||||||
buffers_prefix.cpp
|
buffers_prefix.cpp
|
||||||
buffers_range.cpp
|
buffers_range.cpp
|
||||||
buffers_suffix.cpp
|
buffers_suffix.cpp
|
||||||
|
336
test/beast/core/buffers_generator.cpp
Normal file
336
test/beast/core/buffers_generator.cpp
Normal 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
|
Reference in New Issue
Block a user