mirror of
https://github.com/boostorg/beast.git
synced 2025-07-30 12:57:31 +02:00
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:
10
CHANGELOG.md
10
CHANGELOG.md
@ -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:
|
||||
|
@ -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]]]
|
||||
|
@ -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>
|
||||
|
578
include/boost/beast/core/detail/impl/read.hpp
Normal file
578
include/boost/beast/core/detail/impl/read.hpp
Normal 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
|
241
include/boost/beast/core/detail/read.hpp
Normal file
241
include/boost/beast/core/detail/read.hpp
Normal 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
|
31
include/boost/beast/core/detail/stream_algorithm.hpp
Normal file
31
include/boost/beast/core/detail/stream_algorithm.hpp
Normal 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
|
@ -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){}
|
||||
|
||||
|
@ -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
@ -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
|
||||
|
@ -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
|
||||
|
111
test/beast/core/detail_read.cpp
Normal file
111
test/beast/core/detail_read.cpp
Normal 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
|
@ -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 {
|
||||
|
Reference in New Issue
Block a user