Refactor HTTP operations (API Change):

* Revise stream algorithm javadocs

* HTTP stream algorithms now use beast::read and
  beast::async_read with a custom completion condition.

API Changes:

* HTTP stream algorithms return the number of bytes transferred
  from the stream. Previously, they returned the number of bytes
  consumed by the parser.

Actions Required:

* Callers depending on the return value of http::read or
  http::async_read overloads should adjust the usage of
  the returned value as needed.
This commit is contained in:
Vinnie Falco
2018-12-05 14:18:57 -08:00
parent c11682032b
commit 35e4f321bc
13 changed files with 1597 additions and 823 deletions

View File

@ -3,6 +3,16 @@ Version 197:
* Improvements to echo-op example
* Crawler example clears the response before each read
API Changes:
* Refactor HTTP operations
Actions Required:
* Callers depending on the return value of http::read or
http::async_read overloads should adjust the usage of
the returned value as needed.
--------------------------------------------------------------------------------
Version 196:

View File

@ -55,6 +55,7 @@
[def __AsyncReadStream__ [@boost:/doc/html/boost_asio/reference/AsyncReadStream.html [*AsyncReadStream]]]
[def __AsyncWriteStream__ [@boost:/doc/html/boost_asio/reference/AsyncWriteStream.html [*AsyncWriteStream]]]
[def __CompletionHandler__ [@boost:/doc/html/boost_asio/reference/CompletionHandler.html [*CompletionHandler]]]
[def __CompletionCondition__ [@boost:/doc/html/boost_asio/reference/CompletionCondition.html [*CompletionCondition]]]
[def __ConnectCondition__ [@boost:/doc/html/boost_asio/reference/ConnectCondition.html [*ConnectCondition]]]
[def __ConstBufferSequence__ [@boost:/doc/html/boost_asio/reference/ConstBufferSequence.html [*ConstBufferSequence]]]
[def __EndpointSequence__ [@boost:/doc/html/boost_asio/reference/EndpointSequence.html [*EndpointSequence]]]

View File

@ -18,6 +18,9 @@
<xsl:value-of select="type"/>
<xsl:text> ``[link beast.concepts.BufferSequence [*BufferSequence]]``</xsl:text>
</xsl:when>
<xsl:when test="declname = 'CompletionCondition' or type = 'class CompletionCondition'">
<xsl:text>class __CompletionCondition__</xsl:text>
</xsl:when>
<xsl:when test="declname = 'CompletionHandler' or type = 'class CompletionHandler'">
<xsl:text>class __CompletionHandler__</xsl:text>
</xsl:when>

View File

@ -0,0 +1,578 @@
//
// 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_DETAIL_IMPL_READ_HPP
#define BOOST_BEAST_DETAIL_IMPL_READ_HPP
#include <boost/beast/core/flat_static_buffer.hpp>
#include <boost/asio/basic_stream_socket.hpp>
namespace boost {
namespace beast {
namespace detail {
// The number of bytes in the stack buffer when using non-blocking.
static std::size_t constexpr default_max_stack_buffer = 16384;
//------------------------------------------------------------------------------
// read into a dynamic buffer until the
// condition is met or an error occurs
template<
class Stream,
class DynamicBuffer,
class Condition,
class Handler>
class read_op : public net::coroutine
{
Stream& s_;
net::executor_work_guard<decltype(
std::declval<Stream&>().get_executor())> wg_;
DynamicBuffer& b_;
Condition cond_;
Handler h_;
std::size_t total_ = 0;
public:
read_op(read_op&&) = default;
read_op(read_op const&) = delete;
template<class DeducedHandler>
read_op(
Stream& s,
DynamicBuffer& b,
Condition cond,
DeducedHandler&& h)
: s_(s)
, wg_(s_.get_executor())
, b_(b)
, cond_(cond)
, h_(std::forward<DeducedHandler>(h))
{
(*this)({}, 0, false);
}
void
operator()(
error_code ec,
std::size_t bytes_transferred,
bool cont = true);
//
using allocator_type =
net::associated_allocator_t<Handler>;
using executor_type = net::associated_executor_t<
Handler, decltype(std::declval<Stream&>().get_executor())>;
allocator_type
get_allocator() const noexcept
{
return net::get_associated_allocator(h_);
}
executor_type
get_executor() const noexcept
{
return net::get_associated_executor(
h_, s_.get_executor());
}
friend
void* asio_handler_allocate(
std::size_t size, read_op* op)
{
using net::asio_handler_allocate;
return asio_handler_allocate(
size, std::addressof(op->h_));
}
friend
void asio_handler_deallocate(
void* p, std::size_t size, read_op* op)
{
using net::asio_handler_deallocate;
asio_handler_deallocate(
p, size, std::addressof(op->h_));
}
template<class Function>
friend
void asio_handler_invoke(Function&& f, read_op* op)
{
using net::asio_handler_invoke;
asio_handler_invoke(f, std::addressof(op->h_));
}
};
template<
class Stream, class DynamicBuffer,
class Condition, class Handler>
void
read_op<Stream, DynamicBuffer, Condition, Handler>::
operator()(
error_code ec,
std::size_t bytes_transferred,
bool cont)
{
std::size_t max_size;
std::size_t max_prepare;
BOOST_ASIO_CORO_REENTER(*this)
{
max_size = cond_(ec, total_, b_);
max_prepare = std::min<std::size_t>(
std::max<std::size_t>(
512, b_.capacity() - b_.size()),
std::min<std::size_t>(
max_size, b_.max_size() - b_.size()));
while(max_prepare > 0)
{
BOOST_ASIO_CORO_YIELD
s_.async_read_some(
b_.prepare(max_prepare), std::move(*this));
b_.commit(bytes_transferred);
total_ += bytes_transferred;
max_size = cond_(ec, total_, b_);
max_prepare = std::min<std::size_t>(
std::max<std::size_t>(
512, b_.capacity() - b_.size()),
std::min<std::size_t>(
max_size, b_.max_size() - b_.size()));
}
if(! cont)
{
BOOST_ASIO_CORO_YIELD
net::post(s_.get_executor(),
beast::bind_front_handler(
std::move(*this), ec, total_));
}
h_(ec, total_);
}
}
//------------------------------------------------------------------------------
#ifdef BOOST_BEAST_ENABLE_NON_BLOCKING
// EXPERIMENTAL
// optimized non-blocking read algorithm
template<
class Protocol,
class DynamicBuffer,
class Condition,
class Handler>
class read_non_blocking_op : public net::coroutine
{
net::basic_stream_socket<Protocol>& s_;
net::executor_work_guard<decltype(
s_.get_executor())> wg_;
DynamicBuffer& b_;
Condition cond_;
Handler h_;
std::size_t limit_;
std::size_t total_ = 0;
public:
read_non_blocking_op(read_non_blocking_op&&) = default;
read_non_blocking_op(read_non_blocking_op const&) = delete;
template<class DeducedHandler>
read_non_blocking_op(
net::basic_stream_socket<Protocol>& s,
DynamicBuffer& b,
Condition cond,
DeducedHandler&& h)
: s_(s)
, wg_(s_.get_executor())
, b_(b)
, cond_(cond)
, h_(std::forward<DeducedHandler>(h))
{
(*this)({}, false);
}
void
operator()(error_code ec, bool cont = true);
//
using allocator_type =
net::associated_allocator_t<Handler>;
using executor_type = net::associated_executor_t<
Handler, decltype(s_.get_executor())>;
allocator_type
get_allocator() const noexcept
{
return net::get_associated_allocator(h_);
}
executor_type
get_executor() const noexcept
{
return net::get_associated_executor(
h_, s_.get_executor());
}
friend
void* asio_handler_allocate(
std::size_t size, read_non_blocking_op* op)
{
using net::asio_handler_allocate;
return asio_handler_allocate(
size, std::addressof(op->h_));
}
friend
void asio_handler_deallocate(void* p,
std::size_t size, read_non_blocking_op* op)
{
using net::asio_handler_deallocate;
asio_handler_deallocate(
p, size, std::addressof(op->h_));
}
template<class Function>
friend
void asio_handler_invoke(
Function&& f, read_non_blocking_op* op)
{
using net::asio_handler_invoke;
asio_handler_invoke(f, std::addressof(op->h_));
}
};
template<
class Protocol, class DynamicBuffer,
class Condition, class Handler>
void
read_non_blocking_op<
Protocol, DynamicBuffer, Condition, Handler>::
operator()(error_code ec, bool cont)
{
std::size_t n;
std::size_t bytes_transferred;
BOOST_ASIO_CORO_REENTER(*this)
{
limit_ = cond_(ec, total_, b_);
for(;;)
{
n = detail::min<std::size_t>(
limit_, b_.max_size() - b_.size());
if(n == 0)
break;
BOOST_ASIO_CORO_YIELD
s_.async_wait(
net::socket_base::wait_read, std::move(*this));
if(b_.size() <= default_max_stack_buffer)
{
flat_static_buffer<
default_max_stack_buffer> sb;
bytes_transferred = net::buffer_copy(
sb.prepare(b_.size()), b_.data());
sb.commit(bytes_transferred);
b_.consume(bytes_transferred);
//detail::shrink_to_fit(b_);
n = detail::min<std::size_t>(
limit_,
sb.capacity() - sb.size(),
b_.max_size() - sb.size());
BOOST_ASSERT(n > 0);
bytes_transferred =
s_.read_some(sb.prepare(n), ec);
sb.commit(bytes_transferred);
total_ += bytes_transferred;
limit_ = cond_(ec, total_, sb);
b_.commit(net::buffer_copy(
b_.prepare(sb.size()), sb.data()));
}
else
{
n = detail::min<std::size_t>(
limit_,
s_.available(),
b_.max_size() - b_.size(),
std::max<std::size_t>(
512, b_.capacity() - b_.size()));
BOOST_ASSERT(n > 0);
bytes_transferred = s_.read_some(
b_.prepare(n), ec);
b_.commit(bytes_transferred);
total_ += bytes_transferred;
limit_ = cond_(ec, total_, b_);
}
}
if(! cont)
{
BOOST_ASIO_CORO_YIELD
net::post(s_.get_executor(),
beast::bind_front_handler(
std::move(*this), ec, total_));
}
h_(ec, total_);
}
}
#endif
//------------------------------------------------------------------------------
template<
class AsyncReadStream,
class DynamicBuffer,
class CompletionCondition,
class ReadHandler,
class>
BOOST_ASIO_INITFN_RESULT_TYPE(
ReadHandler, void(error_code, std::size_t))
async_read(
AsyncReadStream& stream,
DynamicBuffer& buffer,
CompletionCondition cond,
ReadHandler&& handler)
{
static_assert(is_async_read_stream<AsyncReadStream>::value,
"AsyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
static_assert(
detail::is_invocable<CompletionCondition,
void(error_code&, std::size_t, DynamicBuffer&)>::value,
"CompletionCondition requirements not met");
BOOST_BEAST_HANDLER_INIT(
ReadHandler, void(error_code, std::size_t));
detail::read_op<
AsyncReadStream,
DynamicBuffer,
CompletionCondition,
BOOST_ASIO_HANDLER_TYPE(
ReadHandler, void(error_code, std::size_t))>(
stream, buffer, std::move(cond),
std::move(init.completion_handler));
return init.result.get();
}
#ifdef BOOST_BEAST_ENABLE_NON_BLOCKING
template<
class Protocol,
class DynamicBuffer,
class CompletionCondition,
class ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
ReadHandler, void(error_code, std::size_t))
async_read(
net::basic_stream_socket<Protocol>& socket,
DynamicBuffer& buffer,
CompletionCondition cond,
ReadHandler&& handler)
{
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
static_assert(
detail::is_invocable<CompletionCondition,
void(error_code&, std::size_t, DynamicBuffer&)>::value,
"CompletionCondition requirements not met");
BOOST_BEAST_HANDLER_INIT(
ReadHandler, void(error_code, std::size_t));
if(socket.non_blocking())
{
detail::read_non_blocking_op<
Protocol,
DynamicBuffer,
CompletionCondition,
BOOST_ASIO_HANDLER_TYPE(
ReadHandler, void(error_code, std::size_t))>(
socket, buffer, std::move(cond),
std::move(init.completion_handler));
}
else
{
detail::read_op<
decltype(socket),
DynamicBuffer,
CompletionCondition,
BOOST_ASIO_HANDLER_TYPE(
ReadHandler, void(error_code, std::size_t))>(
socket, buffer, std::move(cond),
std::move(init.completion_handler));
}
return init.result.get();
}
#endif
//------------------------------------------------------------------------------
template<
class SyncReadStream,
class DynamicBuffer,
class CompletionCondition,
class>
std::size_t
read(
SyncReadStream& stream,
DynamicBuffer& buffer,
CompletionCondition cond)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
static_assert(
detail::is_invocable<CompletionCondition,
void(error_code&, std::size_t, DynamicBuffer&)>::value,
"CompletionCondition requirements not met");
error_code ec;
auto const bytes_transferred = read(
stream, buffer, std::move(cond), ec);
if(ec)
BOOST_THROW_EXCEPTION(system_error{ec});
return bytes_transferred;
}
template<
class SyncReadStream,
class DynamicBuffer,
class CompletionCondition,
class>
std::size_t
read(
SyncReadStream& stream,
DynamicBuffer& buffer,
CompletionCondition cond,
error_code& ec)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
static_assert(
detail::is_invocable<CompletionCondition,
void(error_code&, std::size_t, DynamicBuffer&)>::value,
"CompletionCondition requirements not met");
ec = {};
std::size_t total = 0;
std::size_t max_size;
std::size_t max_prepare;
max_size = cond(ec, total, buffer);
max_prepare = std::min<std::size_t>(
std::max<std::size_t>(
512, buffer.capacity() - buffer.size()),
std::min<std::size_t>(
max_size, buffer.max_size() - buffer.size()));
while(max_prepare > 0)
{
std::size_t const bytes_transferred =
stream.read_some(buffer.prepare(max_prepare), ec);
buffer.commit(bytes_transferred);
total += bytes_transferred;
max_size = cond(ec, total, buffer);
max_prepare = std::min<std::size_t>(
std::max<std::size_t>(
512, buffer.capacity() - buffer.size()),
std::min<std::size_t>(
max_size, buffer.max_size() - buffer.size()));
}
return total;
}
#ifdef BOOST_BEAST_ENABLE_NON_BLOCKING
template<
class Protocol,
class DynamicBuffer,
class CompletionCondition>
std::size_t
read(
net::basic_stream_socket<Protocol>& socket,
DynamicBuffer& buffer,
CompletionCondition cond,
error_code& ec)
{
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
static_assert(
detail::is_invocable<CompletionCondition,
void(error_code&, std::size_t, DynamicBuffer&)>::value,
"CompletionCondition requirements not met");
ec = {};
std::size_t n;
std::size_t limit;
std::size_t total = 0;
std::size_t bytes_transferred;
limit = cond(ec, total, buffer);
for(;;)
{
n = detail::min<std::size_t>(
limit, buffer.max_size() - buffer.size());
if(n == 0)
break;
socket.non_blocking(false);
socket.wait(net::socket_base::wait_read, ec);
socket.non_blocking(true);
if(ec)
{
limit = cond(ec, total, buffer);
}
else if(buffer.size() <= default_max_stack_buffer)
{
flat_static_buffer<
default_max_stack_buffer> sb;
bytes_transferred = net::buffer_copy(
sb.prepare(buffer.size()), buffer.data());
sb.commit(bytes_transferred);
buffer.consume(bytes_transferred);
//detail::shrink_to_fit(buffer);
n = detail::min<std::size_t>(
limit,
sb.capacity() - sb.size(),
buffer.max_size() - sb.size());
BOOST_ASSERT(n > 0);
bytes_transferred =
socket.read_some(sb.prepare(n), ec);
if(ec != net::error::would_block)
{
sb.commit(bytes_transferred);
total += bytes_transferred;
limit = cond(ec, total, sb);
}
buffer.commit(net::buffer_copy(
buffer.prepare(sb.size()), sb.data()));
}
else
{
n = detail::min<std::size_t>(
limit,
socket.available(),
buffer.max_size() - buffer.size(),
std::max<std::size_t>(
512, buffer.capacity() - buffer.size()));
BOOST_ASSERT(n > 0);
bytes_transferred = socket.read_some(
buffer.prepare(n), ec);
if(ec != net::error::would_block)
{
buffer.commit(bytes_transferred);
total += bytes_transferred;
limit = cond(ec, total, buffer);
}
}
}
return total;
}
#endif
} // detail
} // beast
} // boost
#endif

View File

@ -0,0 +1,241 @@
//
// 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_DETAIL_READ_HPP
#define BOOST_BEAST_DETAIL_READ_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/detail/stream_algorithm.hpp>
namespace boost {
namespace beast {
namespace detail {
//------------------------------------------------------------------------------
/** Read data into a dynamic buffer from a stream until a condition is met.
This function is used to read from a stream into a dynamic buffer until
a condition is met. The call will block until one of the following is true:
@li The specified dynamic buffer sequence is full (that is, it has
reached its currently configured maximum size).
@li The `completion_condition` function object returns 0.
This operation is implemented in terms of zero or more calls to the
stream's `read_some` function.
@param stream The stream from which the data is to be read. The type
must support the <em>SyncReadStream</em> requirements.
@param buffer The dynamic buffer sequence into which the data will be read.
@param completion_condition The function object to be called to determine
whether the read operation is complete. The function object must be invocable
with this signature:
@code
std::size_t
completion_condition(
// Modifiable result of latest read_some operation.
error_code& ec,
// Number of bytes transferred so far.
std::size_t bytes_transferred
// The dynamic buffer used to store the bytes read
DynamicBuffer& buffer
);
@endcode
A non-zero return value indicates the maximum number of bytes to be read on
the next call to the stream's `read_some` function. A return value of 0
from the completion condition indicates that the read operation is complete;
in this case the optionally modifiable error passed to the completion
condition will be delivered to the caller as an exception.
@returns The number of bytes transferred from the stream.
@throws net::system_error Thrown on failure.
*/
template<
class SyncReadStream,
class DynamicBuffer,
class CompletionCondition
#if ! BOOST_BEAST_DOXYGEN
, class = typename std::enable_if<
is_sync_read_stream<SyncReadStream>::value &&
net::is_dynamic_buffer<DynamicBuffer>::value &&
detail::is_invocable<CompletionCondition,
void(error_code&, std::size_t, DynamicBuffer&)>::value
>::type
#endif
>
std::size_t
read(
SyncReadStream& stream,
DynamicBuffer& buffer,
CompletionCondition completion_condition);
/** Read data into a dynamic buffer from a stream until a condition is met.
This function is used to read from a stream into a dynamic buffer until
a condition is met. The call will block until one of the following is true:
@li The specified dynamic buffer sequence is full (that is, it has
reached its currently configured maximum size).
@li The `completion_condition` function object returns 0.
This operation is implemented in terms of zero or more calls to the
stream's `read_some` function.
@param stream The stream from which the data is to be read. The type
must support the <em>SyncReadStream</em> requirements.
@param buffer The dynamic buffer sequence into which the data will be read.
@param completion_condition The function object to be called to determine
whether the read operation is complete. The function object must be invocable
with this signature:
@code
std::size_t
completion_condition(
// Modifiable result of latest read_some operation.
error_code& ec,
// Number of bytes transferred so far.
std::size_t bytes_transferred
// The dynamic buffer used to store the bytes read
DynamicBuffer& buffer
);
@endcode
A non-zero return value indicates the maximum number of bytes to be read on
the next call to the stream's `read_some` function. A return value of 0
from the completion condition indicates that the read operation is complete;
in this case the optionally modifiable error passed to the completion
condition will be delivered to the caller.
@returns The number of bytes transferred from the stream.
*/
template<
class SyncReadStream,
class DynamicBuffer,
class CompletionCondition
#if ! BOOST_BEAST_DOXYGEN
, class = typename std::enable_if<
is_sync_read_stream<SyncReadStream>::value &&
net::is_dynamic_buffer<DynamicBuffer>::value &&
detail::is_invocable<CompletionCondition,
void(error_code&, std::size_t, DynamicBuffer&)>::value
>::type
#endif
>
std::size_t
read(
SyncReadStream& stream,
DynamicBuffer& buffer,
CompletionCondition completion_condition,
error_code& ec);
/** Asynchronously read data into a dynamic buffer from a stream until a condition is met.
This function is used to asynchronously read from a stream into a dynamic
buffer until a condition is met. The function call always returns immediately.
The asynchronous operation will continue until one of the following is true:
@li The specified dynamic buffer sequence is full (that is, it has
reached its currently configured maximum size).
@li The `completion_condition` function object returns 0.
This operation is implemented in terms of zero or more calls to the stream's
`async_read_some` function, and is known as a <em>composed operation</em>. The
program must ensure that the stream performs no other read operations (such
as `async_read`, the stream's `async_read_some` function, or any other composed
operations that perform reads) until this operation completes.
@param stream The stream from which the data is to be read. The type must
support the <em>AsyncReadStream</em> requirements.
@param buffer The dynamic buffer sequence into which the data will be read.
Ownership of the object is retained by the caller, which must guarantee
that it remains valid until the handler is called.
@param completion_condition The function object to be called to determine
whether the read operation is complete. The function object must be invocable
with this signature:
@code
std::size_t
completion_condition(
// Modifiable result of latest async_read_some operation.
error_code& ec,
// Number of bytes transferred so far.
std::size_t bytes_transferred,
// The dynamic buffer used to store the bytes read
DynamicBuffer& buffer
);
@endcode
A non-zero return value indicates the maximum number of bytes to be read on
the next call to the stream's `async_read_some` function. A return value of 0
from the completion condition indicates that the read operation is complete;
in this case the optionally modifiable error passed to the completion
condition will be delivered to the completion handler.
@param handler The handler to be called when the read operation completes.
The handler will be moved as needed. The handler must be invocable with
this function signature:
@code
void
handler(
error_code const& ec, // Result of operation.
std::size_t bytes_transferred // Number of bytes copied into
// the dynamic buffer. If an error
// occurred, this will be the number
// of bytes successfully transferred
// prior to the error.
);
@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::io_context::post()`.
*/
template<
class AsyncReadStream,
class DynamicBuffer,
class CompletionCondition,
class ReadHandler
#if ! BOOST_BEAST_DOXYGEN
, class = typename std::enable_if<
is_async_read_stream<AsyncReadStream>::value &&
net::is_dynamic_buffer<DynamicBuffer>::value &&
detail::is_invocable<CompletionCondition,
void(error_code&, std::size_t, DynamicBuffer&)>::value
>::type
#endif
>
BOOST_ASIO_INITFN_RESULT_TYPE(
ReadHandler, void(error_code, std::size_t))
async_read(
AsyncReadStream& stream,
DynamicBuffer& buffer,
CompletionCondition completion_condition,
ReadHandler&& handler);
} // detail
} // beast
} // boost
#include <boost/beast/core/detail/impl/read.hpp>
#endif

View File

@ -0,0 +1,31 @@
//
// 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_DETAIL_STREAM_ALGORITHM_HPP
#define BOOST_BEAST_DETAIL_STREAM_ALGORITHM_HPP
// Include most of what is needed to write stream algorithms
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/beast/core/type_traits.hpp>
#include <boost/beast/core/detail/buffer.hpp>
#include <boost/asio/associated_allocator.hpp>
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/asio/executor_work_guard.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
#include <boost/asio/handler_invoke_hook.hpp>
#include <boost/asio/post.hpp>
#include <boost/assert.hpp>
#include <boost/throw_exception.hpp>
#include <cstdlib>
#endif

View File

@ -24,8 +24,22 @@ namespace boost {
namespace beast {
namespace detail {
// variadic min
template<class T>
T constexpr min(T t)
{
return t;
}
template<class T, class...Tn>
T constexpr min(T t0, T t1, Tn... tn)
{
return (t0 < t1) ?
(detail::min)(t0, tn...) :
(detail::min)(t1, tn...);
}
template<class U>
inline
std::size_t constexpr
max_sizeof()
{
@ -33,7 +47,6 @@ max_sizeof()
}
template<class U0, class U1, class... Us>
inline
std::size_t constexpr
max_sizeof()
{
@ -43,7 +56,6 @@ max_sizeof()
}
template<class U>
inline
std::size_t constexpr
max_alignof()
{
@ -85,7 +97,6 @@ using aligned_union_t =
//------------------------------------------------------------------------------
template<class T>
inline
void
accept_rv(T){}

View File

@ -14,23 +14,10 @@
#include <boost/beast/http/error.hpp>
#include <boost/beast/http/parser.hpp>
#include <boost/beast/http/read.hpp>
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/handler_ptr.hpp>
#include <boost/beast/core/read_size.hpp>
#include <boost/beast/core/type_traits.hpp>
#include <boost/beast/core/detail/buffer.hpp>
#include <boost/asio/associated_allocator.hpp>
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/beast/core/detail/read.hpp>
#include <boost/beast/core/detail/stream_algorithm.hpp>
#include <boost/asio/error.hpp>
#include <boost/asio/executor_work_guard.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
#include <boost/asio/handler_invoke_hook.hpp>
#include <boost/asio/post.hpp>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/optional.hpp>
#include <boost/throw_exception.hpp>
namespace boost {
namespace beast {
@ -38,297 +25,129 @@ namespace http {
namespace detail {
//------------------------------------------------------------------------------
// The default maximum number of bytes to transfer in a single operation.
static std::size_t constexpr default_max_transfer_size = 65536;
template<class Stream, class DynamicBuffer,
bool isRequest, class Derived, class Handler>
class read_some_op
: public net::coroutine
template<
class DynamicBuffer,
bool isRequest, class Derived,
class Condition>
static
std::size_t
parse_until(
DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser,
error_code& ec,
Condition cond)
{
Stream& s_;
net::executor_work_guard<decltype(
std::declval<Stream&>().get_executor())> wg_;
DynamicBuffer& b_;
basic_parser<isRequest, Derived>& p_;
std::size_t bytes_transferred_ = 0;
Handler h_;
bool cont_ = false;
public:
read_some_op(read_some_op&&) = default;
read_some_op(read_some_op const&) = delete;
template<class DeducedHandler>
read_some_op(DeducedHandler&& h, Stream& s,
DynamicBuffer& b, basic_parser<isRequest, Derived>& p)
: s_(s)
, wg_(s_.get_executor())
, b_(b)
, p_(p)
, h_(std::forward<DeducedHandler>(h))
if(ec == net::error::eof)
{
}
using allocator_type =
net::associated_allocator_t<Handler>;
allocator_type
get_allocator() const noexcept
{
return (net::get_associated_allocator)(h_);
}
using executor_type = net::associated_executor_t<
Handler, decltype(std::declval<Stream&>().get_executor())>;
executor_type
get_executor() const noexcept
{
return (net::get_associated_executor)(
h_, s_.get_executor());
}
void
operator()(
error_code ec,
std::size_t bytes_transferred = 0,
bool cont = true);
friend
bool asio_handler_is_continuation(read_some_op* op)
{
using net::asio_handler_is_continuation;
return op->cont_ ? true :
asio_handler_is_continuation(
std::addressof(op->h_));
}
template<class Function>
friend
void asio_handler_invoke(Function&& f, read_some_op* op)
{
using net::asio_handler_invoke;
asio_handler_invoke(f, std::addressof(op->h_));
}
};
template<class Stream, class DynamicBuffer,
bool isRequest, class Derived, class Handler>
void
read_some_op<Stream, DynamicBuffer,
isRequest, Derived, Handler>::
operator()(
error_code ec,
std::size_t bytes_transferred,
bool cont)
{
cont_ = cont;
BOOST_ASIO_CORO_REENTER(*this)
{
if(b_.size() == 0)
goto do_read;
for(;;)
if(parser.got_some())
{
// parse
{
auto const used = p_.put(b_.data(), ec);
bytes_transferred_ += used;
b_.consume(used);
}
if(ec != http::error::need_more)
break;
do_read:
BOOST_ASIO_CORO_YIELD
{
// VFALCO This was read_size_or_throw
auto const size = read_size(b_, 65536);
if(size == 0)
{
ec = error::buffer_overflow;
goto upcall;
}
auto const mb =
beast::detail::dynamic_buffer_prepare(
b_, size, ec, error::buffer_overflow);
if(ec)
goto upcall;
s_.async_read_some(*mb, std::move(*this));
}
if(ec == net::error::eof)
{
BOOST_ASSERT(bytes_transferred == 0);
if(p_.got_some())
{
// caller sees EOF on next read
ec.assign(0, ec.category());
p_.put_eof(ec);
if(ec)
goto upcall;
BOOST_ASSERT(p_.is_done());
goto upcall;
}
ec = error::end_of_stream;
break;
}
if(ec)
break;
b_.commit(bytes_transferred);
// caller sees EOF on next read
ec = {};
parser.put_eof(ec);
BOOST_ASSERT(ec || parser.is_done());
}
upcall:
if(! cont_)
else
{
BOOST_ASIO_CORO_YIELD
net::post(
s_.get_executor(),
beast::bind_front_handler(std::move(*this),
ec, bytes_transferred_));
ec = error::end_of_stream;
}
h_(ec, bytes_transferred_);
return 0;
}
if(ec)
return 0;
if(parser.is_done())
return 0;
if(buffer.size() > 0)
{
auto const bytes_used =
parser.put(buffer.data(), ec);
// total = total + bytes_used; // VFALCO Can't do this in a condition
buffer.consume(bytes_used);
if(ec == http::error::need_more)
{
if(buffer.size() >= buffer.max_size())
{
ec = http::error::buffer_overflow;
return 0;
}
ec = {};
}
else if(ec || cond())
{
return 0;
}
}
return default_max_transfer_size;
}
//------------------------------------------------------------------------------
struct parser_is_done
// predicate is true on any forward parser progress
template<bool isRequest, class Derived>
struct read_some_condition
{
template<bool isRequest, class Derived>
bool
operator()(basic_parser<
isRequest, Derived> const& p) const
basic_parser<isRequest, Derived>& parser;
template<class DynamicBuffer>
std::size_t
operator()(error_code& ec, std::size_t,
DynamicBuffer& buffer)
{
return p.is_done();
return detail::parse_until(
buffer, parser, ec,
[]
{
return true;
});
}
};
struct parser_is_header_done
// predicate is true when parser header is complete
template<bool isRequest, class Derived>
struct read_header_condition
{
template<bool isRequest, class Derived>
bool
operator()(basic_parser<
isRequest, Derived> const& p) const
basic_parser<isRequest, Derived>& parser;
template<class DynamicBuffer>
std::size_t
operator()(error_code& ec, std::size_t,
DynamicBuffer& buffer)
{
return p.is_header_done();
return detail::parse_until(
buffer, parser, ec,
[this]
{
return parser.is_header_done();
});
}
};
template<class Stream, class DynamicBuffer,
bool isRequest, class Derived, class Condition,
class Handler>
class read_op
: public net::coroutine
// predicate is true when parser message is complete
template<bool isRequest, class Derived>
struct read_all_condition
{
Stream& s_;
net::executor_work_guard<decltype(
std::declval<Stream&>().get_executor())> wg_;
DynamicBuffer& b_;
basic_parser<isRequest, Derived>& p_;
std::size_t bytes_transferred_ = 0;
Handler h_;
bool cont_ = false;
basic_parser<isRequest, Derived>& parser;
public:
read_op(read_op&&) = default;
read_op(read_op const&) = delete;
template<class DeducedHandler>
read_op(DeducedHandler&& h, Stream& s,
DynamicBuffer& b, basic_parser<isRequest,
Derived>& p)
: s_(s)
, wg_(s_.get_executor())
, b_(b)
, p_(p)
, h_(std::forward<DeducedHandler>(h))
template<class DynamicBuffer>
std::size_t
operator()(error_code& ec, std::size_t,
DynamicBuffer& buffer)
{
}
using allocator_type =
net::associated_allocator_t<Handler>;
allocator_type
get_allocator() const noexcept
{
return (net::get_associated_allocator)(h_);
}
using executor_type = net::associated_executor_t<
Handler, decltype(std::declval<Stream&>().get_executor())>;
executor_type
get_executor() const noexcept
{
return (net::get_associated_executor)(
h_, s_.get_executor());
}
void
operator()(
error_code ec,
std::size_t bytes_transferred = 0,
bool cont = true);
friend
bool asio_handler_is_continuation(read_op* op)
{
using net::asio_handler_is_continuation;
return op->cont_ ? true :
asio_handler_is_continuation(
std::addressof(op->h_));
}
template<class Function>
friend
void asio_handler_invoke(Function&& f, read_op* op)
{
using net::asio_handler_invoke;
asio_handler_invoke(f, std::addressof(op->h_));
return detail::parse_until(
buffer, parser, ec,
[this]
{
return parser.is_done();
});
}
};
template<class Stream, class DynamicBuffer,
bool isRequest, class Derived, class Condition,
class Handler>
void
read_op<Stream, DynamicBuffer,
isRequest, Derived, Condition, Handler>::
operator()(
error_code ec,
std::size_t bytes_transferred,
bool cont)
{
cont_ = cont;
BOOST_ASIO_CORO_REENTER(*this)
{
if(Condition{}(p_))
{
BOOST_ASIO_CORO_YIELD
net::post(s_.get_executor(),
beast::bind_front_handler(std::move(*this), ec));
goto upcall;
}
for(;;)
{
BOOST_ASIO_CORO_YIELD
async_read_some(
s_, b_, p_, std::move(*this));
bytes_transferred_ += bytes_transferred;
if(ec)
goto upcall;
if(Condition{}(p_))
goto upcall;
}
upcall:
h_(ec, bytes_transferred_);
}
}
//------------------------------------------------------------------------------
template<class Stream, class DynamicBuffer,
template<
class Stream, class DynamicBuffer,
bool isRequest, class Body, class Allocator,
class Handler>
class Handler>
class read_msg_op
: public net::coroutine
{
@ -341,23 +160,17 @@ class read_msg_op
struct data
{
Stream& s;
net::executor_work_guard<decltype(
std::declval<Stream&>().get_executor())> wg;
DynamicBuffer& b;
message_type& m;
parser_type p;
std::size_t bytes_transferred = 0;
bool cont = false;
data(Handler const&, Stream& s_,
DynamicBuffer& b_, message_type& m_)
data(
Handler const&,
Stream& s_,
message_type& m_)
: s(s_)
, wg(s.get_executor())
, b(b_)
, m(m_)
, p(std::move(m))
{
p.eager(true);
}
};
@ -367,45 +180,59 @@ public:
read_msg_op(read_msg_op&&) = default;
read_msg_op(read_msg_op const&) = delete;
template<class DeducedHandler, class... Args>
read_msg_op(DeducedHandler&& h, Stream& s, Args&&... args)
: d_(std::forward<DeducedHandler>(h),
s, std::forward<Args>(args)...)
template<class DeducedHandler>
read_msg_op(
Stream& s,
DynamicBuffer& b,
message_type& m,
DeducedHandler&& h)
: d_(std::forward<DeducedHandler>(h), s, m)
{
}
using allocator_type =
net::associated_allocator_t<Handler>;
allocator_type
get_allocator() const noexcept
{
return (net::get_associated_allocator)(d_.handler());
}
using executor_type = net::associated_executor_t<
Handler, decltype(std::declval<Stream&>().get_executor())>;
executor_type
get_executor() const noexcept
{
return (net::get_associated_executor)(
d_.handler(), d_->s.get_executor());
http::async_read(s, b, d_->p, std::move(*this));
}
void
operator()(
error_code ec,
std::size_t bytes_transferred = 0,
bool cont = true);
std::size_t bytes_transferred);
//
using allocator_type =
net::associated_allocator_t<Handler>;
using executor_type = net::associated_executor_t<
Handler, decltype(std::declval<Stream&>().get_executor())>;
allocator_type
get_allocator() const noexcept
{
return net::get_associated_allocator(d_.handler());
}
executor_type
get_executor() const noexcept
{
return net::get_associated_executor(
d_.handler(), d_->s.get_executor());
}
friend
bool asio_handler_is_continuation(read_msg_op* op)
void* asio_handler_allocate(
std::size_t size, read_msg_op* op)
{
using net::asio_handler_is_continuation;
return op->d_->cont ? true :
asio_handler_is_continuation(
std::addressof(op->d_.handler()));
using net::asio_handler_allocate;
return asio_handler_allocate(
size, std::addressof(op->d_.handler()));
}
friend
void asio_handler_deallocate(
void* p, std::size_t size, read_msg_op* op)
{
using net::asio_handler_deallocate;
asio_handler_deallocate(
p, size, std::addressof(op->d_.handler()));
}
template<class Function>
@ -425,35 +252,12 @@ read_msg_op<Stream, DynamicBuffer,
isRequest, Body, Allocator, Handler>::
operator()(
error_code ec,
std::size_t bytes_transferred,
bool cont)
std::size_t bytes_transferred)
{
auto& d = *d_;
d.cont = cont;
BOOST_ASIO_CORO_REENTER(*this)
{
for(;;)
{
BOOST_ASIO_CORO_YIELD
async_read_some(
d.s, d.b, d.p, std::move(*this));
if(ec)
goto upcall;
d.bytes_transferred +=
bytes_transferred;
if(d.p.is_done())
{
d.m = d.p.release();
goto upcall;
}
}
upcall:
bytes_transferred = d.bytes_transferred;
{
auto wg = std::move(d.wg);
d_.invoke(ec, bytes_transferred);
}
}
if(! ec)
d.m = d.p.release();
d_.invoke(ec, bytes_transferred);
}
} // detail
@ -470,12 +274,12 @@ read_some(
DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
static_assert(
is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
BOOST_ASSERT(! parser.is_done());
error_code ec;
auto const bytes_transferred =
read_some(stream, buffer, parser, ec);
@ -495,60 +299,15 @@ read_some(
basic_parser<isRequest, Derived>& parser,
error_code& ec)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
static_assert(
is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
BOOST_ASSERT(! parser.is_done());
std::size_t bytes_transferred = 0;
if(buffer.size() == 0)
goto do_read;
for(;;)
{
// invoke parser
{
auto const n = parser.put(buffer.data(), ec);
bytes_transferred += n;
buffer.consume(n);
if(! ec)
break;
if(ec != http::error::need_more)
break;
}
do_read:
auto const size = read_size(buffer, 65536);
if(size == 0)
{
ec = error::buffer_overflow;
break;
}
auto const mb =
beast::detail::dynamic_buffer_prepare(
buffer, size, ec, error::buffer_overflow);
if(ec)
break;
auto const n = stream.read_some(*mb, ec);
if(ec == net::error::eof)
{
BOOST_ASSERT(n == 0);
if(parser.got_some())
{
// caller sees EOF on next read
parser.put_eof(ec);
if(ec)
break;
BOOST_ASSERT(parser.is_done());
break;
}
ec = error::end_of_stream;
break;
}
if(ec)
break;
buffer.commit(n);
}
return bytes_transferred;
return beast::detail::read(stream, buffer,
detail::read_some_condition<
isRequest, Derived>{parser}, ec);
}
template<
@ -564,19 +323,18 @@ async_read_some(
basic_parser<isRequest, Derived>& parser,
ReadHandler&& handler)
{
static_assert(is_async_read_stream<AsyncReadStream>::value,
static_assert(
is_async_read_stream<AsyncReadStream>::value,
"AsyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
BOOST_ASSERT(! parser.is_done());
BOOST_BEAST_HANDLER_INIT(
ReadHandler, void(error_code, std::size_t));
detail::read_some_op<AsyncReadStream,
DynamicBuffer, isRequest, Derived, BOOST_ASIO_HANDLER_TYPE(
ReadHandler, void(error_code, std::size_t))>{
std::move(init.completion_handler), stream, buffer, parser}(
{}, 0, false);
beast::detail::async_read(stream, buffer,
detail::read_some_condition<
isRequest, Derived>{parser}, std::move(
init.completion_handler));
return init.result.get();
}
@ -592,7 +350,8 @@ read_header(
DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
static_assert(
is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
@ -616,27 +375,16 @@ read_header(
basic_parser<isRequest, Derived>& parser,
error_code& ec)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
static_assert(
is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
parser.eager(false);
if(parser.is_header_done())
{
ec.assign(0, ec.category());
return 0;
}
std::size_t bytes_transferred = 0;
do
{
bytes_transferred += read_some(
stream, buffer, parser, ec);
if(ec)
return bytes_transferred;
}
while(! parser.is_header_done());
return bytes_transferred;
return beast::detail::read(stream, buffer,
detail::read_header_condition<
isRequest, Derived>{parser}, ec);
}
template<
@ -652,19 +400,19 @@ async_read_header(
basic_parser<isRequest, Derived>& parser,
ReadHandler&& handler)
{
static_assert(is_async_read_stream<AsyncReadStream>::value,
static_assert(
is_async_read_stream<AsyncReadStream>::value,
"AsyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
parser.eager(false);
BOOST_BEAST_HANDLER_INIT(
ReadHandler, void(error_code, std::size_t));
detail::read_op<AsyncReadStream, DynamicBuffer,
isRequest, Derived, detail::parser_is_header_done,
BOOST_ASIO_HANDLER_TYPE(ReadHandler, void(error_code, std::size_t))>{
std::move(init.completion_handler), stream,
buffer, parser}({}, 0, false);
parser.eager(false);
beast::detail::async_read(stream, buffer,
detail::read_header_condition<
isRequest, Derived>{parser}, std::move(
init.completion_handler));
return init.result.get();
}
@ -680,7 +428,8 @@ read(
DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
static_assert(
is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
@ -704,27 +453,16 @@ read(
basic_parser<isRequest, Derived>& parser,
error_code& ec)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
static_assert(
is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
parser.eager(true);
if(parser.is_done())
{
ec.assign(0, ec.category());
return 0;
}
std::size_t bytes_transferred = 0;
do
{
bytes_transferred += read_some(
stream, buffer, parser, ec);
if(ec)
return bytes_transferred;
}
while(! parser.is_done());
return bytes_transferred;
return beast::detail::read(stream, buffer,
detail::read_all_condition<
isRequest, Derived>{parser}, ec);
}
template<
@ -740,19 +478,19 @@ async_read(
basic_parser<isRequest, Derived>& parser,
ReadHandler&& handler)
{
static_assert(is_async_read_stream<AsyncReadStream>::value,
static_assert(
is_async_read_stream<AsyncReadStream>::value,
"AsyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
parser.eager(true);
BOOST_BEAST_HANDLER_INIT(
ReadHandler, void(error_code, std::size_t));
detail::read_op<AsyncReadStream, DynamicBuffer,
isRequest, Derived, detail::parser_is_done,
BOOST_ASIO_HANDLER_TYPE(ReadHandler, void(error_code, std::size_t))>{
std::move(init.completion_handler), stream, buffer, parser}(
{}, 0, false);
parser.eager(true);
beast::detail::async_read(stream, buffer,
detail::read_all_condition<
isRequest, Derived>{parser}, std::move(
init.completion_handler));
return init.result.get();
}
@ -768,7 +506,8 @@ read(
DynamicBuffer& buffer,
message<isRequest, Body, basic_fields<Allocator>>& msg)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
static_assert(
is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
@ -796,7 +535,8 @@ read(
message<isRequest, Body, basic_fields<Allocator>>& msg,
error_code& ec)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
static_assert(
is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
@ -805,7 +545,7 @@ read(
"Body requirements not met");
static_assert(is_body_reader<Body>::value,
"BodyReader requirements not met");
parser<isRequest, Body, Allocator> p{std::move(msg)};
parser<isRequest, Body, Allocator> p(std::move(msg));
p.eager(true);
auto const bytes_transferred =
read(stream, buffer, p.base(), ec);
@ -828,7 +568,8 @@ async_read(
message<isRequest, Body, basic_fields<Allocator>>& msg,
ReadHandler&& handler)
{
static_assert(is_async_read_stream<AsyncReadStream>::value,
static_assert(
is_async_read_stream<AsyncReadStream>::value,
"AsyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
@ -844,9 +585,9 @@ async_read(
DynamicBuffer,
isRequest, Body, Allocator,
BOOST_ASIO_HANDLER_TYPE(
ReadHandler, void(error_code, std::size_t))>{
std::move(init.completion_handler), stream, buffer, msg}(
{}, 0, false);
ReadHandler, void(error_code, std::size_t))>(
stream, buffer, msg, std::move(
init.completion_handler));
return init.result.get();
}

File diff suppressed because it is too large Load Diff

View File

@ -44,6 +44,7 @@ add_executable (tests-beast-core
static_buffer.cpp
string_param.cpp
type_traits.cpp
detail_read.cpp
detail/base64.cpp
detail/clamp.cpp
detail/lean_tuple.cpp

View File

@ -34,6 +34,7 @@ local SOURCES =
string.cpp
string_param.cpp
type_traits.cpp
detail_read.cpp
detail/base64.cpp
detail/clamp.cpp
detail/lean_tuple.cpp

View File

@ -0,0 +1,111 @@
//
// Copyright (c) 2018 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
// Test that header file is self-contained.
#include <boost/beast/core/detail/read.hpp>
#include <boost/beast/core/buffers_to_string.hpp>
#include <boost/beast/core/flat_buffer.hpp>
#include <boost/beast/core/ostream.hpp>
#include <boost/beast/_experimental/test/stream.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp>
#include <boost/asio/completion_condition.hpp>
namespace boost {
namespace beast {
namespace detail {
class read_test : public unit_test::suite
{
public:
#if 0
void
testRead()
{
{
net::io_context ioc;
test::stream ts(ioc);
ostream(ts.buffer()) << "test";
error_code ec;
flat_buffer b;
read(ts, b, boost::asio::transfer_at_least(4), ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(buffers_to_string(b.data()) == "test");
}
{
net::io_context ioc;
test::stream ts(ioc);
auto tc = connect(ts);
ostream(ts.buffer()) << "test";
tc.close();
error_code ec;
flat_buffer b;
read(ts, b, boost::asio::transfer_all(), ec);
BEAST_EXPECTS(ec == boost::asio::error::eof, ec.message());
BEAST_EXPECT(buffers_to_string(b.data()) == "test");
}
}
void
testAsyncRead()
{
{
net::io_context ioc;
test::stream ts(ioc);
ostream(ts.buffer()) << "test";
flat_buffer b;
error_code ec;
bool invoked = false;
async_read(ts, b, boost::asio::transfer_at_least(4),
[&invoked, &ec](error_code ec_, std::size_t)
{
ec = ec_;
invoked = true;
});
ioc.run();
BEAST_EXPECT(invoked);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(buffers_to_string(b.data()) == "test");
}
{
net::io_context ioc;
test::stream ts(ioc);
auto tc = connect(ts);
ostream(ts.buffer()) << "test";
tc.close();
flat_buffer b;
error_code ec;
bool invoked = false;
async_read(ts, b, boost::asio::transfer_all(),
[&invoked, &ec](error_code ec_, std::size_t)
{
ec = ec_;
invoked = true;
});
ioc.run();
BEAST_EXPECT(invoked);
BEAST_EXPECTS(ec == boost::asio::error::eof, ec.message());
BEAST_EXPECT(buffers_to_string(b.data()) == "test");
}
}
#endif
void
run() override
{
pass();
//testRead();
//testAsyncRead();
}
};
BEAST_DEFINE_TESTSUITE(beast,core,read);
} // detail
} // beast
} // boost

View File

@ -23,6 +23,7 @@
#include <boost/beast/test/yield_to.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/strand.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <atomic>
namespace boost {