diff --git a/CHANGELOG.md b/CHANGELOG.md
index 13f54b5e..d19ebea0 100644
--- a/CHANGELOG.md
+++ b/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:
diff --git a/doc/qbk/00_main.qbk b/doc/qbk/00_main.qbk
index b9fcad8e..c68a8bd7 100644
--- a/doc/qbk/00_main.qbk
+++ b/doc/qbk/00_main.qbk
@@ -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]]]
diff --git a/doc/xsl/class_detail.xsl b/doc/xsl/class_detail.xsl
index d4ba1e2c..5ef923f5 100644
--- a/doc/xsl/class_detail.xsl
+++ b/doc/xsl/class_detail.xsl
@@ -18,6 +18,9 @@
``[link beast.concepts.BufferSequence [*BufferSequence]]``
+
+ class __CompletionCondition__
+
class __CompletionHandler__
diff --git a/include/boost/beast/core/detail/impl/read.hpp b/include/boost/beast/core/detail/impl/read.hpp
new file mode 100644
index 00000000..89a209e1
--- /dev/null
+++ b/include/boost/beast/core/detail/impl/read.hpp
@@ -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
+#include
+
+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().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
+ read_op(
+ Stream& s,
+ DynamicBuffer& b,
+ Condition cond,
+ DeducedHandler&& h)
+ : s_(s)
+ , wg_(s_.get_executor())
+ , b_(b)
+ , cond_(cond)
+ , h_(std::forward(h))
+ {
+ (*this)({}, 0, false);
+ }
+
+ void
+ operator()(
+ error_code ec,
+ std::size_t bytes_transferred,
+ bool cont = true);
+
+ //
+
+ using allocator_type =
+ net::associated_allocator_t;
+
+ using executor_type = net::associated_executor_t<
+ Handler, decltype(std::declval().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
+ 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::
+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::max(
+ 512, b_.capacity() - b_.size()),
+ std::min(
+ 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::max(
+ 512, b_.capacity() - b_.size()),
+ std::min(
+ 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& s_;
+ net::executor_work_guard 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
+ read_non_blocking_op(
+ net::basic_stream_socket& s,
+ DynamicBuffer& b,
+ Condition cond,
+ DeducedHandler&& h)
+ : s_(s)
+ , wg_(s_.get_executor())
+ , b_(b)
+ , cond_(cond)
+ , h_(std::forward(h))
+ {
+ (*this)({}, false);
+ }
+
+ void
+ operator()(error_code ec, bool cont = true);
+
+ //
+
+ using allocator_type =
+ net::associated_allocator_t;
+
+ 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
+ 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(
+ 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(
+ 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(
+ limit_,
+ s_.available(),
+ b_.max_size() - b_.size(),
+ std::max(
+ 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::value,
+ "AsyncReadStream requirements not met");
+ static_assert(
+ net::is_dynamic_buffer::value,
+ "DynamicBuffer requirements not met");
+ static_assert(
+ detail::is_invocable::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& socket,
+ DynamicBuffer& buffer,
+ CompletionCondition cond,
+ ReadHandler&& handler)
+{
+ static_assert(
+ net::is_dynamic_buffer::value,
+ "DynamicBuffer requirements not met");
+ static_assert(
+ detail::is_invocable::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::value,
+ "SyncReadStream requirements not met");
+ static_assert(
+ net::is_dynamic_buffer::value,
+ "DynamicBuffer requirements not met");
+ static_assert(
+ detail::is_invocable::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::value,
+ "SyncReadStream requirements not met");
+ static_assert(
+ net::is_dynamic_buffer::value,
+ "DynamicBuffer requirements not met");
+ static_assert(
+ detail::is_invocable::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::max(
+ 512, buffer.capacity() - buffer.size()),
+ std::min(
+ 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::max(
+ 512, buffer.capacity() - buffer.size()),
+ std::min(
+ 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& socket,
+ DynamicBuffer& buffer,
+ CompletionCondition cond,
+ error_code& ec)
+{
+ static_assert(
+ net::is_dynamic_buffer::value,
+ "DynamicBuffer requirements not met");
+ static_assert(
+ detail::is_invocable::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(
+ 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(
+ 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(
+ limit,
+ socket.available(),
+ buffer.max_size() - buffer.size(),
+ std::max(
+ 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
diff --git a/include/boost/beast/core/detail/read.hpp b/include/boost/beast/core/detail/read.hpp
new file mode 100644
index 00000000..eb16221a
--- /dev/null
+++ b/include/boost/beast/core/detail/read.hpp
@@ -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
+#include
+
+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 SyncReadStream 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::value &&
+ net::is_dynamic_buffer::value &&
+ detail::is_invocable::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 SyncReadStream 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::value &&
+ net::is_dynamic_buffer::value &&
+ detail::is_invocable::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 composed operation. 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 AsyncReadStream 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::value &&
+ net::is_dynamic_buffer::value &&
+ detail::is_invocable::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
+
+#endif
diff --git a/include/boost/beast/core/detail/stream_algorithm.hpp b/include/boost/beast/core/detail/stream_algorithm.hpp
new file mode 100644
index 00000000..f531c629
--- /dev/null
+++ b/include/boost/beast/core/detail/stream_algorithm.hpp
@@ -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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#endif
diff --git a/include/boost/beast/core/detail/type_traits.hpp b/include/boost/beast/core/detail/type_traits.hpp
index 59488f13..e0bcb841 100644
--- a/include/boost/beast/core/detail/type_traits.hpp
+++ b/include/boost/beast/core/detail/type_traits.hpp
@@ -24,8 +24,22 @@ namespace boost {
namespace beast {
namespace detail {
+// variadic min
+template
+T constexpr min(T t)
+{
+ return t;
+}
+
+template
+T constexpr min(T t0, T t1, Tn... tn)
+{
+ return (t0 < t1) ?
+ (detail::min)(t0, tn...) :
+ (detail::min)(t1, tn...);
+}
+
template
-inline
std::size_t constexpr
max_sizeof()
{
@@ -33,7 +47,6 @@ max_sizeof()
}
template
-inline
std::size_t constexpr
max_sizeof()
{
@@ -43,7 +56,6 @@ max_sizeof()
}
template
-inline
std::size_t constexpr
max_alignof()
{
@@ -85,7 +97,6 @@ using aligned_union_t =
//------------------------------------------------------------------------------
template
-inline
void
accept_rv(T){}
diff --git a/include/boost/beast/http/impl/read.ipp b/include/boost/beast/http/impl/read.ipp
index 78773af1..597a3b56 100644
--- a/include/boost/beast/http/impl/read.ipp
+++ b/include/boost/beast/http/impl/read.ipp
@@ -14,23 +14,10 @@
#include
#include
#include
-#include
#include
-#include
-#include
-#include
-#include
-#include
-#include
+#include
+#include
#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
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 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& parser,
+ error_code& ec,
+ Condition cond)
{
- Stream& s_;
- net::executor_work_guard().get_executor())> wg_;
- DynamicBuffer& b_;
- basic_parser& 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
- read_some_op(DeducedHandler&& h, Stream& s,
- DynamicBuffer& b, basic_parser& p)
- : s_(s)
- , wg_(s_.get_executor())
- , b_(b)
- , p_(p)
- , h_(std::forward(h))
+ if(ec == net::error::eof)
{
- }
-
- using allocator_type =
- net::associated_allocator_t;
-
- allocator_type
- get_allocator() const noexcept
- {
- return (net::get_associated_allocator)(h_);
- }
-
- using executor_type = net::associated_executor_t<
- Handler, decltype(std::declval().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
- 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
-void
-read_some_op::
-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
+struct read_some_condition
{
- template
- bool
- operator()(basic_parser<
- isRequest, Derived> const& p) const
+ basic_parser& parser;
+
+ template
+ 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
+struct read_header_condition
{
- template
- bool
- operator()(basic_parser<
- isRequest, Derived> const& p) const
+ basic_parser& parser;
+
+ template
+ 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 read_op
- : public net::coroutine
+// predicate is true when parser message is complete
+template
+struct read_all_condition
{
- Stream& s_;
- net::executor_work_guard().get_executor())> wg_;
- DynamicBuffer& b_;
- basic_parser& p_;
- std::size_t bytes_transferred_ = 0;
- Handler h_;
- bool cont_ = false;
+ basic_parser& parser;
-public:
- read_op(read_op&&) = default;
- read_op(read_op const&) = delete;
-
- template
- read_op(DeducedHandler&& h, Stream& s,
- DynamicBuffer& b, basic_parser& p)
- : s_(s)
- , wg_(s_.get_executor())
- , b_(b)
- , p_(p)
- , h_(std::forward(h))
+ template
+ std::size_t
+ operator()(error_code& ec, std::size_t,
+ DynamicBuffer& buffer)
{
- }
-
- using allocator_type =
- net::associated_allocator_t;
-
- allocator_type
- get_allocator() const noexcept
- {
- return (net::get_associated_allocator)(h_);
- }
-
- using executor_type = net::associated_executor_t<
- Handler, decltype(std::declval().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
- 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
-void
-read_op::
-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 Handler>
class read_msg_op
: public net::coroutine
{
@@ -341,23 +160,17 @@ class read_msg_op
struct data
{
Stream& s;
- net::executor_work_guard().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
- read_msg_op(DeducedHandler&& h, Stream& s, Args&&... args)
- : d_(std::forward(h),
- s, std::forward(args)...)
+ template
+ read_msg_op(
+ Stream& s,
+ DynamicBuffer& b,
+ message_type& m,
+ DeducedHandler&& h)
+ : d_(std::forward(h), s, m)
{
- }
-
- using allocator_type =
- net::associated_allocator_t;
-
- allocator_type
- get_allocator() const noexcept
- {
- return (net::get_associated_allocator)(d_.handler());
- }
-
- using executor_type = net::associated_executor_t<
- Handler, decltype(std::declval().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;
+
+ using executor_type = net::associated_executor_t<
+ Handler, decltype(std::declval().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
@@ -425,35 +252,12 @@ read_msg_op::
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& parser)
{
- static_assert(is_sync_read_stream::value,
+ static_assert(
+ is_sync_read_stream::value,
"SyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer::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& parser,
error_code& ec)
{
- static_assert(is_sync_read_stream::value,
+ static_assert(
+ is_sync_read_stream::value,
"SyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer::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& parser,
ReadHandler&& handler)
{
- static_assert(is_async_read_stream::value,
+ static_assert(
+ is_async_read_stream::value,
"AsyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer::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{
- 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& parser)
{
- static_assert(is_sync_read_stream::value,
+ static_assert(
+ is_sync_read_stream::value,
"SyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer::value,
@@ -616,27 +375,16 @@ read_header(
basic_parser& parser,
error_code& ec)
{
- static_assert(is_sync_read_stream::value,
+ static_assert(
+ is_sync_read_stream::value,
"SyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer::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& parser,
ReadHandler&& handler)
{
- static_assert(is_async_read_stream::value,
+ static_assert(
+ is_async_read_stream::value,
"AsyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer::value,
"DynamicBuffer requirements not met");
- parser.eager(false);
BOOST_BEAST_HANDLER_INIT(
ReadHandler, void(error_code, std::size_t));
- detail::read_op{
- 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& parser)
{
- static_assert(is_sync_read_stream::value,
+ static_assert(
+ is_sync_read_stream::value,
"SyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer::value,
@@ -704,27 +453,16 @@ read(
basic_parser& parser,
error_code& ec)
{
- static_assert(is_sync_read_stream::value,
+ static_assert(
+ is_sync_read_stream::value,
"SyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer::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& parser,
ReadHandler&& handler)
{
- static_assert(is_async_read_stream::value,
+ static_assert(
+ is_async_read_stream::value,
"AsyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer::value,
"DynamicBuffer requirements not met");
- parser.eager(true);
BOOST_BEAST_HANDLER_INIT(
ReadHandler, void(error_code, std::size_t));
- detail::read_op{
- 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>& msg)
{
- static_assert(is_sync_read_stream::value,
+ static_assert(
+ is_sync_read_stream::value,
"SyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer::value,
@@ -796,7 +535,8 @@ read(
message>& msg,
error_code& ec)
{
- static_assert(is_sync_read_stream::value,
+ static_assert(
+ is_sync_read_stream::value,
"SyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer::value,
@@ -805,7 +545,7 @@ read(
"Body requirements not met");
static_assert(is_body_reader::value,
"BodyReader requirements not met");
- parser p{std::move(msg)};
+ parser p(std::move(msg));
p.eager(true);
auto const bytes_transferred =
read(stream, buffer, p.base(), ec);
@@ -828,7 +568,8 @@ async_read(
message>& msg,
ReadHandler&& handler)
{
- static_assert(is_async_read_stream::value,
+ static_assert(
+ is_async_read_stream::value,
"AsyncReadStream requirements not met");
static_assert(
net::is_dynamic_buffer::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();
}
diff --git a/include/boost/beast/http/read.hpp b/include/boost/beast/http/read.hpp
index 1d6698f9..1aa01385 100644
--- a/include/boost/beast/http/read.hpp
+++ b/include/boost/beast/http/read.hpp
@@ -20,48 +20,54 @@ namespace boost {
namespace beast {
namespace http {
+//------------------------------------------------------------------------------
+
/** Read part of a message from a stream using a parser.
- This function is used to read part of a message from a stream into a
- subclass of @ref basic_parser.
- The call will block until one of the following conditions is true:
+ This function is used to read part of a message from a stream into an
+ instance of @ref basic_parser. The call will block until one of the
+ following conditions is true:
@li A call to @ref basic_parser::put with a non-empty buffer sequence
- is successful.
+ is successful.
@li An error occurs.
- This operation is implemented in terms of one or
- more calls to the stream's `read_some` function.
- The implementation may read additional octets that lie past the
- end of the message being read. This additional data is stored
- in the dynamic buffer, which must be retained for subsequent reads.
+ This operation is implemented in terms of one or more calls to the stream's
+ `read_some` function. The implementation may read additional bytes from
+ the stream that lie past the end of the message being read. These additional
+ bytes are stored in the dynamic buffer, which must be preserved for
+ subsequent reads.
- If the stream returns the error `net::error::eof` indicating the
- end of file during a read, the error returned from this function will be:
+ If the end of file error is received while reading from the stream, then
+ the error returned from this function will be:
- @li @ref error::end_of_stream if no octets were parsed, or
+ @li @ref error::end_of_stream if no bytes were parsed, or
- @li @ref error::partial_message if any octets were parsed but the
- message was incomplete, otherwise:
+ @li @ref error::partial_message if any bytes were parsed but the
+ message was incomplete, otherwise:
- @li A successful result. A subsequent attempt to read will
- return @ref error::end_of_stream
+ @li A successful result. The next attempt to read will return
+ @ref error::end_of_stream
- @param stream The stream from which the data is to be read.
- The type must support the @b SyncReadStream concept.
+ @param stream The stream from which the data is to be read. The type must
+ meet the SyncReadStream requirements.
- @param buffer A @b DynamicBuffer holding additional bytes
- read by the implementation from the stream. This is both
- an input and an output parameter; on entry, any data in the
- dynamic buffer's input sequence will be given to the parser
- first.
+ @param buffer Storage for additional bytes read by the implementation from
+ the stream. This is both an input and an output parameter; on entry, the
+ parser will be presented with any remaining data in the dynamic buffer's
+ readable bytes sequence first. The type must meet the DynamicBuffer
+ requirements.
@param parser The parser to use.
- @return The number of bytes transferred to the parser.
+ @return The number of bytes transferred from the stream.
@throws system_error Thrown on failure.
+
+ @note The function returns the total number of bytes transferred from the
+ stream. This may be zero for the case where there is sufficient pre-existing
+ message data in the dynamic buffer.
*/
template<
class SyncReadStream,
@@ -75,50 +81,50 @@ read_some(
/** Read part of a message from a stream using a parser.
- This function is used to read part of a message from a stream into a
- subclass of @ref basic_parser.
- The call will block until one of the following conditions is true:
+ This function is used to read part of a message from a stream into an
+ instance of @ref basic_parser. The call will block until one of the
+ following conditions is true:
@li A call to @ref basic_parser::put with a non-empty buffer sequence
- is successful.
+ is successful.
@li An error occurs.
- This operation is implemented in terms of one or
- more calls to the stream's `read_some` function.
- The implementation may read additional octets that lie past the
- end of the message being read. This additional data is stored
- in the dynamic buffer, which must be retained for subsequent reads.
+ This operation is implemented in terms of one or more calls to the stream's
+ `read_some` function. The implementation may read additional bytes from
+ the stream that lie past the end of the message being read. These additional
+ bytes are stored in the dynamic buffer, which must be preserved for
+ subsequent reads.
- If the stream returns the error `net::error::eof` indicating the
- end of file during a read, the error returned from this function will be:
+ If the end of file error is received while reading from the stream, then
+ the error returned from this function will be:
- @li @ref error::end_of_stream if no octets were parsed, or
+ @li @ref error::end_of_stream if no bytes were parsed, or
- @li @ref error::partial_message if any octets were parsed but the
- message was incomplete, otherwise:
+ @li @ref error::partial_message if any bytes were parsed but the
+ message was incomplete, otherwise:
- @li A successful result. A subsequent attempt to read will
- return @ref error::end_of_stream
+ @li A successful result. The next attempt to read will return
+ @ref error::end_of_stream
- The function returns the number of bytes processed from the dynamic
- buffer. The caller should remove these bytes by calling `consume` on
- the dynamic buffer, regardless of any error.
+ @param stream The stream from which the data is to be read. The type must
+ support the SyncReadStream requirements.
- @param stream The stream from which the data is to be read.
- The type must support the @b SyncReadStream concept.
-
- @param buffer A @b DynamicBuffer holding additional bytes
- read by the implementation from the stream. This is both
- an input and an output parameter; on entry, any data in the
- dynamic buffer's input sequence will be given to the parser
- first.
+ @param buffer Storage for additional bytes read by the implementation from
+ the stream. This is both an input and an output parameter; on entry, the
+ parser will be presented with any remaining data in the dynamic buffer's
+ readable bytes sequence first. The type must meet the DynamicBuffer
+ requirements.
@param parser The parser to use.
@param ec Set to the error, if any occurred.
- @return The number of bytes transferred to the parser.
+ @return The number of bytes transferred from the stream.
+
+ @note The function returns the total number of bytes transferred from the
+ stream. This may be zero for the case where there is sufficient pre-existing
+ message data in the dynamic buffer.
*/
template<
class SyncReadStream,
@@ -134,63 +140,64 @@ read_some(
/** Read part of a message asynchronously from a stream using a parser.
This function is used to asynchronously read part of a message from
- a stream into a subclass of @ref basic_parser.
- The function call always returns immediately. The asynchronous operation
- will continue until one of the following conditions is true:
+ a stream into an instance of @ref basic_parser. The function call
+ always returns immediately. The asynchronous operation will continue
+ until one of the following conditions is true:
@li A call to @ref basic_parser::put with a non-empty buffer sequence
- is successful.
+ is successful.
@li An error occurs.
- This operation is implemented in terms of zero or more calls to
- the next layer's `async_read_some` function, and is known as a
- composed operation. The program must ensure that the
- stream performs no other reads until this operation completes.
- The implementation may read additional octets that lie past the
- end of the object being parsed. This additional data is stored
- in the stream buffer, which may be used in subsequent calls.
+ This operation is implemented in terms of zero or more calls to the
+ next layer's `async_read_some` function, and is known as a composed
+ operation. The program must ensure that the stream performs no other
+ reads until this operation completes. The implementation may read additional
+ bytes from the stream that lie past the end of the message being read.
+ These additional bytes are stored in the dynamic buffer, which must be
+ preserved for subsequent reads.
- If the stream returns the error `net::error::eof` indicating the
- end of file during a read, the error returned from this function will be:
+ If the end of file error is received while reading from the stream, then
+ the error returned from this function will be:
- @li @ref error::end_of_stream if no octets were parsed, or
+ @li @ref error::end_of_stream if no bytes were parsed, or
- @li @ref error::partial_message if any octets were parsed but the
- message was incomplete, otherwise:
+ @li @ref error::partial_message if any bytes were parsed but the
+ message was incomplete, otherwise:
- @li A successful result. A subsequent attempt to read will
- return @ref error::end_of_stream
+ @li A successful result. The next attempt to read will return
+ @ref error::end_of_stream
- @param stream The stream from which the data is to be read.
- The type must support the @b AsyncReadStream concept.
+ @param stream The stream from which the data is to be read. The type
+ must meet the AsyncReadStream requirements.
- @param buffer A @b DynamicBuffer holding additional bytes
- read by the implementation from the stream. This is both
- an input and an output parameter; on entry, any data in the
- dynamic buffer's input sequence will be given to the parser
- first.
+ @param buffer Storage for additional bytes read by the implementation from
+ the stream. This is both an input and an output parameter; on entry, the
+ parser will be presented with any remaining data in the dynamic buffer's
+ readable bytes sequence first. The type must meet the DynamicBuffer
+ requirements. The object must remain valid at least until the handler
+ is called; ownership is not transferred.
- @param parser The parser to use.
- The object must remain valid at least until the
- handler is called; ownership is not transferred.
+ @param parser The parser to use. The object must remain valid at least until
+ the handler is called; ownership is not transferred.
- @param handler Invoked when the operation completes.
- The handler may be moved or copied as needed.
- The equivalent function signature of the handler must be:
- @code void handler(
+ @param handler Invoked when the operation completes. The handler will
+ be moved as needed. The handler must be invocable with the following
+ signature:
+ @code
+ void handler(
error_code const& error, // result of operation
- std::size_t bytes_transferred // the number of bytes transferred to the parser
- ); @endcode
+ std::size_t bytes_transferred // the total number of bytes transferred from the stream
+ );
+ @endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
manner equivalent to using `net::io_context::post`.
- The completion handler will receive as a parameter the number
- of octets processed from the dynamic buffer. The octets should
- be removed by calling `consume` on the dynamic buffer after
- the read completes, regardless of any error.
+ @note The completion handler will receive as a parameter the total number
+ of bytes transferred from the stream. This may be zero for the case where
+ there is sufficient pre-existing message data in the dynamic buffer.
*/
template<
class AsyncReadStream,
@@ -207,50 +214,52 @@ async_read_some(
//------------------------------------------------------------------------------
-/** Read a header from a stream using a parser.
+/** Read a complete message header from a stream using a parser.
- This function is used to read a header from a stream into a subclass
- of @ref basic_parser.
- The call will block until one of the following conditions is true:
+ This function is used to read a complete message header from a stream
+ into an instance of @ref basic_parser. The call will block until one of the
+ following conditions is true:
@li @ref basic_parser::is_header_done returns `true`
@li An error occurs.
- This operation is implemented in terms of one or
- more calls to the stream's `read_some` function.
- The implementation may read additional octets that lie past the
- end of the message being read. This additional data is stored
- in the dynamic buffer, which must be retained for subsequent reads.
+ This operation is implemented in terms of one or more calls to the stream's
+ `read_some` function. The implementation may read additional bytes from
+ the stream that lie past the end of the message being read. These additional
+ bytes are stored in the dynamic buffer, which must be preserved for
+ subsequent reads.
- If the stream returns the error `net::error::eof` indicating the
- end of file during a read, the error returned from this function will be:
+ If the end of file error is received while reading from the stream, then
+ the error returned from this function will be:
- @li @ref error::end_of_stream if no octets were parsed, or
+ @li @ref error::end_of_stream if no bytes were parsed, or
- @li @ref error::partial_message if any octets were parsed but the
- message was incomplete, otherwise:
+ @li @ref error::partial_message if any bytes were parsed but the
+ message was incomplete, otherwise:
- @li A successful result. A subsequent attempt to read will
- return @ref error::end_of_stream
+ @li A successful result. The next attempt to read will return
+ @ref error::end_of_stream
- @param stream The stream from which the data is to be read.
- The type must support the @b SyncReadStream concept.
+ @param stream The stream from which the data is to be read. The type must
+ meet the SyncReadStream requirements.
- @param buffer A @b DynamicBuffer holding additional bytes
- read by the implementation from the stream. This is both
- an input and an output parameter; on entry, any data in the
- dynamic buffer's input sequence will be given to the parser
- first.
+ @param buffer Storage for additional bytes read by the implementation from
+ the stream. This is both an input and an output parameter; on entry, the
+ parser will be presented with any remaining data in the dynamic buffer's
+ readable bytes sequence first. The type must meet the DynamicBuffer
+ requirements.
@param parser The parser to use.
- @return The number of bytes transferred to the parser.
+ @return The number of bytes transferred from the stream.
@throws system_error Thrown on failure.
- @note The implementation will call @ref basic_parser::eager
- with the value `false` on the parser passed in.
+ @note The function returns the total number of bytes transferred from the
+ stream. This may be zero for the case where there is sufficient pre-existing
+ message data in the dynamic buffer. The implementation will call
+ @ref basic_parser::eager with the value `false` on the parser passed in.
*/
template<
class SyncReadStream,
@@ -262,50 +271,52 @@ read_header(
DynamicBuffer& buffer,
basic_parser& parser);
-/** Read a header from a stream using a parser.
+/** Read a complete message header from a stream using a parser.
- This function is used to read a header from a stream into a subclass
- of @ref basic_parser.
- The call will block until one of the following conditions is true:
+ This function is used to read a complete message header from a stream
+ into an instance of @ref basic_parser. The call will block until one of the
+ following conditions is true:
@li @ref basic_parser::is_header_done returns `true`
@li An error occurs.
- This operation is implemented in terms of one or
- more calls to the stream's `read_some` function.
- The implementation may read additional octets that lie past the
- end of the message being read. This additional data is stored
- in the dynamic buffer, which must be retained for subsequent reads.
+ This operation is implemented in terms of one or more calls to the stream's
+ `read_some` function. The implementation may read additional bytes from
+ the stream that lie past the end of the message being read. These additional
+ bytes are stored in the dynamic buffer, which must be preserved for
+ subsequent reads.
- If the stream returns the error `net::error::eof` indicating the
- end of file during a read, the error returned from this function will be:
+ If the end of file error is received while reading from the stream, then
+ the error returned from this function will be:
- @li @ref error::end_of_stream if no octets were parsed, or
+ @li @ref error::end_of_stream if no bytes were parsed, or
- @li @ref error::partial_message if any octets were parsed but the
- message was incomplete, otherwise:
+ @li @ref error::partial_message if any bytes were parsed but the
+ message was incomplete, otherwise:
- @li A successful result. A subsequent attempt to read will
- return @ref error::end_of_stream
+ @li A successful result. The next attempt to read will return
+ @ref error::end_of_stream
- @param stream The stream from which the data is to be read.
- The type must support the @b SyncReadStream concept.
+ @param stream The stream from which the data is to be read. The type must
+ meet the SyncReadStream requirements.
- @param buffer A @b DynamicBuffer holding additional bytes
- read by the implementation from the stream. This is both
- an input and an output parameter; on entry, any data in the
- dynamic buffer's input sequence will be given to the parser
- first.
+ @param buffer Storage for additional bytes read by the implementation from
+ the stream. This is both an input and an output parameter; on entry, the
+ parser will be presented with any remaining data in the dynamic buffer's
+ readable bytes sequence first. The type must meet the DynamicBuffer
+ requirements.
@param parser The parser to use.
@param ec Set to the error, if any occurred.
- @return The number of bytes transferred to the parser.
+ @return The number of bytes transferred from the stream.
- @note The implementation will call @ref basic_parser::eager
- with the value `false` on the parser passed in.
+ @note The function returns the total number of bytes transferred from the
+ stream. This may be zero for the case where there is sufficient pre-existing
+ message data in the dynamic buffer. The implementation will call
+ @ref basic_parser::eager with the value `false` on the parser passed in.
*/
template<
class SyncReadStream,
@@ -318,63 +329,68 @@ read_header(
basic_parser& parser,
error_code& ec);
-/** Read a header from a stream asynchronously using a parser.
+/** Read a complete message header asynchronously from a stream using a parser.
- This function is used to asynchronously read a header from a stream
- into a subclass of @ref basic_parser.
- The function call always returns immediately. The asynchronous operation
- will continue until one of the following conditions is true:
+ This function is used to asynchronously read a complete message header from
+ a stream into an instance of @ref basic_parser. The function call always
+ returns immediately. The asynchronous operation will continue until one of
+ the following conditions is true:
@li @ref basic_parser::is_header_done returns `true`
@li An error occurs.
- This operation is implemented in terms of one or more calls to
- the stream's `async_read_some` function, and is known as a
- composed operation. The program must ensure that the
- stream performs no other reads until this operation completes.
- The implementation may read additional octets that lie past the
- end of the message being read. This additional data is stored
- in the dynamic buffer, which must be retained for subsequent reads.
+ This operation is implemented in terms of zero or more calls to the
+ next layer's `async_read_some` function, and is known as a composed
+ operation. The program must ensure that the stream performs no other
+ reads until this operation completes. The implementation may read additional
+ bytes from the stream that lie past the end of the message being read.
+ These additional bytes are stored in the dynamic buffer, which must be
+ preserved for subsequent reads.
- If the stream returns the error `net::error::eof` indicating the
- end of file during a read, the error returned from this function will be:
+ If the end of file error is received while reading from the stream, then
+ the error returned from this function will be:
- @li @ref error::end_of_stream if no octets were parsed, or
+ @li @ref error::end_of_stream if no bytes were parsed, or
- @li @ref error::partial_message if any octets were parsed but the
- message was incomplete, otherwise:
+ @li @ref error::partial_message if any bytes were parsed but the
+ message was incomplete, otherwise:
- @li A successful result. A subsequent attempt to read will
- return @ref error::end_of_stream
+ @li A successful result. The next attempt to read will return
+ @ref error::end_of_stream
- @param stream The stream from which the data is to be read.
- The type must support the @b AsyncReadStream concept.
+ @param stream The stream from which the data is to be read. The type
+ must meet the AsyncReadStream requirements.
- @param buffer A @b DynamicBuffer holding additional bytes
- read by the implementation from the stream. This is both
- an input and an output parameter; on entry, any data in the
- dynamic buffer's input sequence will be given to the parser
- first.
+ @param buffer Storage for additional bytes read by the implementation from
+ the stream. This is both an input and an output parameter; on entry, the
+ parser will be presented with any remaining data in the dynamic buffer's
+ readable bytes sequence first. The type must meet the DynamicBuffer
+ requirements. The object must remain valid at least until the handler
+ is called; ownership is not transferred.
- @param parser The parser to use.
- The object must remain valid at least until the
- handler is called; ownership is not transferred.
+ @param parser The parser to use. The object must remain valid at least until
+ the handler is called; ownership is not transferred.
- @param handler Invoked when the operation completes.
- The handler may be moved or copied as needed.
- The equivalent function signature of the handler must be:
- @code void handler(
- error_code const& error, // result of operation,
- std::size_t bytes_transferred // the number of bytes transferred to the parser
- ); @endcode
+ @param handler Invoked when the operation completes. The handler will
+ be moved as needed. The handler must be invocable with the following
+ signature:
+ @code
+ void handler(
+ error_code const& error, // result of operation
+ std::size_t bytes_transferred // the total number of bytes transferred from the stream
+ );
+ @endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
manner equivalent to using `net::io_context::post`.
- @note The implementation will call @ref basic_parser::eager
- with the value `false` on the parser passed in.
+ @note The completion handler will receive as a parameter the total number
+ of bytes transferred from the stream. This may be zero for the case where
+ there is sufficient pre-existing message data in the dynamic buffer. The
+ implementation will call @ref basic_parser::eager with the value `false`
+ on the parser passed in.
*/
template<
class AsyncReadStream,
@@ -393,48 +409,50 @@ async_read_header(
/** Read a complete message from a stream using a parser.
- This function is used to read a complete message from a stream into a
- subclass of @ref basic_parser.
- The call will block until one of the following conditions is true:
+ This function is used to read a complete message from a stream into an
+ instance of @ref basic_parser. The call will block until one of the
+ following conditions is true:
@li @ref basic_parser::is_done returns `true`
@li An error occurs.
- This operation is implemented in terms of one or
- more calls to the stream's `read_some` function.
- The implementation may read additional octets that lie past the
- end of the message being read. This additional data is stored
- in the dynamic buffer, which must be retained for subsequent reads.
+ This operation is implemented in terms of one or more calls to the stream's
+ `read_some` function. The implementation may read additional bytes from
+ the stream that lie past the end of the message being read. These additional
+ bytes are stored in the dynamic buffer, which must be preserved for
+ subsequent reads.
- If the stream returns the error `net::error::eof` indicating the
- end of file during a read, the error returned from this function will be:
+ If the end of file error is received while reading from the stream, then
+ the error returned from this function will be:
- @li @ref error::end_of_stream if no octets were parsed, or
+ @li @ref error::end_of_stream if no bytes were parsed, or
- @li @ref error::partial_message if any octets were parsed but the
- message was incomplete, otherwise:
+ @li @ref error::partial_message if any bytes were parsed but the
+ message was incomplete, otherwise:
- @li A successful result. A subsequent attempt to read will
- return @ref error::end_of_stream
+ @li A successful result. The next attempt to read will return
+ @ref error::end_of_stream
- @param stream The stream from which the data is to be read.
- The type must support the @b SyncReadStream concept.
+ @param stream The stream from which the data is to be read. The type must
+ meet the SyncReadStream requirements.
- @param buffer A @b DynamicBuffer holding additional bytes
- read by the implementation from the stream. This is both
- an input and an output parameter; on entry, any data in the
- dynamic buffer's input sequence will be given to the parser
- first.
+ @param buffer Storage for additional bytes read by the implementation from
+ the stream. This is both an input and an output parameter; on entry, the
+ parser will be presented with any remaining data in the dynamic buffer's
+ readable bytes sequence first. The type must meet the DynamicBuffer
+ requirements.
@param parser The parser to use.
- @return The number of bytes transferred to the parser.
+ @return The number of bytes transferred from the stream.
@throws system_error Thrown on failure.
- @note The implementation will call @ref basic_parser::eager
- with the value `true` on the parser passed in.
+ @note The function returns the total number of bytes transferred from the
+ stream. This may be zero for the case where there is sufficient pre-existing
+ message data in the dynamic buffer. The implementation will call
+ @ref basic_parser::eager with the value `true` on the parser passed in.
*/
template<
class SyncReadStream,
@@ -448,48 +466,50 @@ read(
/** Read a complete message from a stream using a parser.
- This function is used to read a complete message from a stream into a
- subclass of @ref basic_parser.
- The call will block until one of the following conditions is true:
+ This function is used to read a complete message from a stream into an
+ instance of @ref basic_parser. The call will block until one of the
+ following conditions is true:
@li @ref basic_parser::is_done returns `true`
@li An error occurs.
- This operation is implemented in terms of one or
- more calls to the stream's `read_some` function.
- The implementation may read additional octets that lie past the
- end of the message being read. This additional data is stored
- in the dynamic buffer, which must be retained for subsequent reads.
+ This operation is implemented in terms of one or more calls to the stream's
+ `read_some` function. The implementation may read additional bytes from
+ the stream that lie past the end of the message being read. These additional
+ bytes are stored in the dynamic buffer, which must be preserved for
+ subsequent reads.
- If the stream returns the error `net::error::eof` indicating the
- end of file during a read, the error returned from this function will be:
+ If the end of file error is received while reading from the stream, then
+ the error returned from this function will be:
- @li @ref error::end_of_stream if no octets were parsed, or
+ @li @ref error::end_of_stream if no bytes were parsed, or
- @li @ref error::partial_message if any octets were parsed but the
- message was incomplete, otherwise:
+ @li @ref error::partial_message if any bytes were parsed but the
+ message was incomplete, otherwise:
- @li A successful result. A subsequent attempt to read will
- return @ref error::end_of_stream
+ @li A successful result. The next attempt to read will return
+ @ref error::end_of_stream
- @param stream The stream from which the data is to be read.
- The type must support the @b SyncReadStream concept.
+ @param stream The stream from which the data is to be read. The type must
+ meet the SyncReadStream requirements.
- @param buffer A @b DynamicBuffer holding additional bytes
- read by the implementation from the stream. This is both
- an input and an output parameter; on entry, any data in the
- dynamic buffer's input sequence will be given to the parser
- first.
+ @param buffer Storage for additional bytes read by the implementation from
+ the stream. This is both an input and an output parameter; on entry, the
+ parser will be presented with any remaining data in the dynamic buffer's
+ readable bytes sequence first. The type must meet the DynamicBuffer
+ requirements.
@param parser The parser to use.
@param ec Set to the error, if any occurred.
- @return The number of bytes transferred to the parser.
+ @return The number of bytes transferred from the stream.
- @note The implementation will call @ref basic_parser::eager
- with the value `true` on the parser passed in.
+ @note The function returns the total number of bytes transferred from the
+ stream. This may be zero for the case where there is sufficient pre-existing
+ message data in the dynamic buffer. The implementation will call
+ @ref basic_parser::eager with the value `true` on the parser passed in.
*/
template<
class SyncReadStream,
@@ -502,63 +522,68 @@ read(
basic_parser& parser,
error_code& ec);
-/** Read a complete message from a stream asynchronously using a parser.
+/** Read a complete message asynchronously from a stream using a parser.
This function is used to asynchronously read a complete message from a
- stream into a subclass of @ref basic_parser.
- The function call always returns immediately. The asynchronous operation
- will continue until one of the following conditions is true:
+ stream into an instance of @ref basic_parser. The function call always
+ returns immediately. The asynchronous operation will continue until one
+ of the following conditions is true:
@li @ref basic_parser::is_done returns `true`
@li An error occurs.
- This operation is implemented in terms of one or more calls to
- the stream's `async_read_some` function, and is known as a
- composed operation. The program must ensure that the
- stream performs no other reads until this operation completes.
- The implementation may read additional octets that lie past the
- end of the message being read. This additional data is stored
- in the dynamic buffer, which must be retained for subsequent reads.
+ This operation is implemented in terms of zero or more calls to the
+ next layer's `async_read_some` function, and is known as a composed
+ operation. The program must ensure that the stream performs no other
+ reads until this operation completes. The implementation may read additional
+ bytes from the stream that lie past the end of the message being read.
+ These additional bytes are stored in the dynamic buffer, which must be
+ preserved for subsequent reads.
- If the stream returns the error `net::error::eof` indicating the
- end of file during a read, the error returned from this function will be:
+ If the end of file error is received while reading from the stream, then
+ the error returned from this function will be:
- @li @ref error::end_of_stream if no octets were parsed, or
+ @li @ref error::end_of_stream if no bytes were parsed, or
- @li @ref error::partial_message if any octets were parsed but the
- message was incomplete, otherwise:
+ @li @ref error::partial_message if any bytes were parsed but the
+ message was incomplete, otherwise:
- @li A successful result. A subsequent attempt to read will
- return @ref error::end_of_stream
+ @li A successful result. The next attempt to read will return
+ @ref error::end_of_stream
- @param stream The stream from which the data is to be read.
- The type must support the @b AsyncReadStream concept.
+ @param stream The stream from which the data is to be read. The type
+ must meet the AsyncReadStream requirements.
- @param buffer A @b DynamicBuffer holding additional bytes
- read by the implementation from the stream. This is both
- an input and an output parameter; on entry, any data in the
- dynamic buffer's input sequence will be given to the parser
- first.
+ @param buffer Storage for additional bytes read by the implementation from
+ the stream. This is both an input and an output parameter; on entry, the
+ parser will be presented with any remaining data in the dynamic buffer's
+ readable bytes sequence first. The type must meet the DynamicBuffer
+ requirements. The object must remain valid at least until the handler
+ is called; ownership is not transferred.
- @param parser The parser to use.
- The object must remain valid at least until the
- handler is called; ownership is not transferred.
+ @param parser The parser to use. The object must remain valid at least until
+ the handler is called; ownership is not transferred.
- @param handler Invoked when the operation completes.
- The handler may be moved or copied as needed.
- The equivalent function signature of the handler must be:
- @code void handler(
- error_code const& error, // result of operation,
- std::size_t bytes_transferred // the number of bytes transferred to the parser
- ); @endcode
+ @param handler Invoked when the operation completes. The handler will
+ be moved as needed. The handler must be invocable with the following
+ signature:
+ @code
+ void handler(
+ error_code const& error, // result of operation
+ std::size_t bytes_transferred // the total number of bytes transferred from the stream
+ );
+ @endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
manner equivalent to using `net::io_context::post`.
- @note The implementation will call @ref basic_parser::eager
- with the value `true` on the parser passed in.
+ @note The completion handler will receive as a parameter the total number
+ of bytes transferred from the stream. This may be zero for the case where
+ there is sufficient pre-existing message data in the dynamic buffer. The
+ implementation will call @ref basic_parser::eager with the value `true`
+ on the parser passed in.
*/
template<
class AsyncReadStream,
@@ -577,47 +602,53 @@ async_read(
/** Read a complete message from a stream.
- This function is used to read a complete message from a stream using HTTP/1.
- The call will block until one of the following conditions is true:
+ This function is used to read a complete message from a stream into an
+ instance of @ref message. The call will block until one of the following
+ conditions is true:
- @li The entire message is read.
+ @li The entire message is read in.
@li An error occurs.
- This operation is implemented in terms of one or
- more calls to the stream's `read_some` function.
- The implementation may read additional octets that lie past the
- end of the message being read. This additional data is stored
- in the dynamic buffer, which must be retained for subsequent reads.
+ This operation is implemented in terms of one or more calls to the stream's
+ `read_some` function. The implementation may read additional bytes from
+ the stream that lie past the end of the message being read. These additional
+ bytes are stored in the dynamic buffer, which must be preserved for
+ subsequent reads.
- If the stream returns the error `net::error::eof` indicating the
- end of file during a read, the error returned from this function will be:
+ If the end of file error is received while reading from the stream, then
+ the error returned from this function will be:
- @li @ref error::end_of_stream if no octets were parsed, or
+ @li @ref error::end_of_stream if no bytes were parsed, or
- @li @ref error::partial_message if any octets were parsed but the
- message was incomplete, otherwise:
+ @li @ref error::partial_message if any bytes were parsed but the
+ message was incomplete, otherwise:
- @li A successful result. A subsequent attempt to read will
- return @ref error::end_of_stream
+ @li A successful result. The next attempt to read will return
+ @ref error::end_of_stream
- @param stream The stream from which the data is to be read.
- The type must support the @b SyncReadStream concept.
+ @param stream The stream from which the data is to be read. The type must
+ meet the SyncReadStream requirements.
- @param buffer A @b DynamicBuffer holding additional bytes
- read by the implementation from the stream. This is both
- an input and an output parameter; on entry, any data in the
- dynamic buffer's input sequence will be given to the parser
- first.
+ @param buffer Storage for additional bytes read by the implementation from
+ the stream. This is both an input and an output parameter; on entry, the
+ parser will be presented with any remaining data in the dynamic buffer's
+ readable bytes sequence first. The type must meet the DynamicBuffer
+ requirements.
- @param msg An object in which to store the message contents.
- This object should not have previous contents, otherwise
- the behavior is undefined.
- The type must be @b MoveAssignable and @b MoveConstructible.
+ @param msg The container in which to store the message contents. This
+ message container should not have previous contents, otherwise the behavior
+ is undefined. The type must be meet the MoveAssignable and
+ MoveConstructible requirements.
- @return The number of bytes transferred to the parser.
+ @return The number of bytes transferred from the stream.
@throws system_error Thrown on failure.
+
+ @note The function returns the total number of bytes transferred from the
+ stream. This may be zero for the case where there is sufficient pre-existing
+ message data in the dynamic buffer. The implementation will call
+ @ref basic_parser::eager with the value `true` on the parser passed in.
*/
template<
class SyncReadStream,
@@ -631,47 +662,53 @@ read(
/** Read a complete message from a stream.
- This function is used to read a complete message from a stream using HTTP/1.
- The call will block until one of the following conditions is true:
+ This function is used to read a complete message from a stream into an
+ instance of @ref message. The call will block until one of the following
+ conditions is true:
- @li The entire message is read.
+ @li The entire message is read in.
@li An error occurs.
- This operation is implemented in terms of one or
- more calls to the stream's `read_some` function.
- The implementation may read additional octets that lie past the
- end of the message being read. This additional data is stored
- in the dynamic buffer, which must be retained for subsequent reads.
+ This operation is implemented in terms of one or more calls to the stream's
+ `read_some` function. The implementation may read additional bytes from
+ the stream that lie past the end of the message being read. These additional
+ bytes are stored in the dynamic buffer, which must be preserved for
+ subsequent reads.
- If the stream returns the error `net::error::eof` indicating the
- end of file during a read, the error returned from this function will be:
+ If the end of file error is received while reading from the stream, then
+ the error returned from this function will be:
- @li @ref error::end_of_stream if no octets were parsed, or
+ @li @ref error::end_of_stream if no bytes were parsed, or
- @li @ref error::partial_message if any octets were parsed but the
- message was incomplete, otherwise:
+ @li @ref error::partial_message if any bytes were parsed but the
+ message was incomplete, otherwise:
- @li A successful result. A subsequent attempt to read will
- return @ref error::end_of_stream
+ @li A successful result. The next attempt to read will return
+ @ref error::end_of_stream
- @param stream The stream from which the data is to be read.
- The type must support the @b SyncReadStream concept.
+ @param stream The stream from which the data is to be read. The type must
+ meet the SyncReadStream requirements.
- @param buffer A @b DynamicBuffer holding additional bytes
- read by the implementation from the stream. This is both
- an input and an output parameter; on entry, any data in the
- dynamic buffer's input sequence will be given to the parser
- first.
+ @param buffer Storage for additional bytes read by the implementation from
+ the stream. This is both an input and an output parameter; on entry, the
+ parser will be presented with any remaining data in the dynamic buffer's
+ readable bytes sequence first. The type must meet the DynamicBuffer
+ requirements.
- @param msg An object in which to store the message contents.
- This object should not have previous contents, otherwise
- the behavior is undefined.
- The type must be @b MoveAssignable and @b MoveConstructible.
+ @param msg The container in which to store the message contents. This
+ message container should not have previous contents, otherwise the behavior
+ is undefined. The type must be meet the MoveAssignable and
+ MoveConstructible requirements.
@param ec Set to the error, if any occurred.
- @return The number of bytes transferred to the parser.
+ @return The number of bytes transferred from the stream.
+
+ @note The function returns the total number of bytes transferred from the
+ stream. This may be zero for the case where there is sufficient pre-existing
+ message data in the dynamic buffer. The implementation will call
+ @ref basic_parser::eager with the value `true` on the parser passed in.
*/
template<
class SyncReadStream,
@@ -684,64 +721,71 @@ read(
message>& msg,
error_code& ec);
-/** Read a complete message from a stream asynchronously.
+/** Read a complete message asynchronously from a stream.
This function is used to asynchronously read a complete message from a
- stream using HTTP/1.
- The function call always returns immediately. The asynchronous operation
- will continue until one of the following conditions is true:
+ stream into an instance of @ref message. The function call always returns
+ immediately. The asynchronous operation will continue until one of the
+ following conditions is true:
- @li The entire message is read.
+ @li The entire message is read in.
@li An error occurs.
- This operation is implemented in terms of one or more calls to
- the stream's `async_read_some` function, and is known as a
- composed operation. The program must ensure that the
- stream performs no other reads until this operation completes.
- The implementation may read additional octets that lie past the
- end of the message being read. This additional data is stored
- in the dynamic buffer, which must be retained for subsequent reads.
+ This operation is implemented in terms of zero or more calls to the
+ next layer's `async_read_some` function, and is known as a composed
+ operation. The program must ensure that the stream performs no other
+ reads until this operation completes. The implementation may read additional
+ bytes from the stream that lie past the end of the message being read.
+ These additional bytes are stored in the dynamic buffer, which must be
+ preserved for subsequent reads.
- If the stream returns the error `net::error::eof` indicating the
- end of file during a read, the error returned from this function will be:
+ If the end of file error is received while reading from the stream, then
+ the error returned from this function will be:
- @li @ref error::end_of_stream if no octets were parsed, or
+ @li @ref error::end_of_stream if no bytes were parsed, or
- @li @ref error::partial_message if any octets were parsed but the
- message was incomplete, otherwise:
+ @li @ref error::partial_message if any bytes were parsed but the
+ message was incomplete, otherwise:
- @li A successful result. A subsequent attempt to read will
- return @ref error::end_of_stream
+ @li A successful result. The next attempt to read will return
+ @ref error::end_of_stream
- @param stream The stream from which the data is to be read.
- The type must support the @b AsyncReadStream concept.
+ @param stream The stream from which the data is to be read. The type
+ must meet the AsyncReadStream requirements.
- @param buffer A @b DynamicBuffer holding additional bytes
- read by the implementation from the stream. This is both
- an input and an output parameter; on entry, any data in the
- dynamic buffer's input sequence will be given to the parser
- first.
+ @param buffer Storage for additional bytes read by the implementation from
+ the stream. This is both an input and an output parameter; on entry, the
+ parser will be presented with any remaining data in the dynamic buffer's
+ readable bytes sequence first. The type must meet the DynamicBuffer
+ requirements. The object must remain valid at least until the handler
+ is called; ownership is not transferred.
- @param msg An object in which to store the message contents.
- This object should not have previous contents, otherwise
- the behavior is undefined.
- The type must be @b MoveAssignable and @b MoveConstructible.
+ @param msg The container in which to store the message contents. This
+ message container should not have previous contents, otherwise the behavior
+ is undefined. The type must be meet the MoveAssignable and
+ MoveConstructible requirements. The object must remain valid
+ at least until the handler is called; ownership is not transferred.
- The object must remain valid at least until the
- handler is called; ownership is not transferred.
-
- @param handler Invoked when the operation completes.
- The handler may be moved or copied as needed.
- The equivalent function signature of the handler must be:
- @code void handler(
- error_code const& error, // result of operation,
- std::size_t bytes_transferred // the number of bytes transferred to the parser
- ); @endcode
+ @param handler Invoked when the operation completes. The handler will
+ be moved as needed. The handler must be invocable with the following
+ signature:
+ @code
+ void handler(
+ error_code const& error, // result of operation
+ std::size_t bytes_transferred // the total number of bytes transferred from the stream
+ );
+ @endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
manner equivalent to using `net::io_context::post`.
+
+ @note The completion handler will receive as a parameter the total number
+ of bytes transferred from the stream. This may be zero for the case where
+ there is sufficient pre-existing message data in the dynamic buffer. The
+ implementation will call @ref basic_parser::eager with the value `true`
+ on the parser passed in.
*/
template<
class AsyncReadStream,
diff --git a/test/beast/core/CMakeLists.txt b/test/beast/core/CMakeLists.txt
index 74435db1..2125f4d5 100644
--- a/test/beast/core/CMakeLists.txt
+++ b/test/beast/core/CMakeLists.txt
@@ -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
diff --git a/test/beast/core/Jamfile b/test/beast/core/Jamfile
index 704355fb..5dbbf695 100644
--- a/test/beast/core/Jamfile
+++ b/test/beast/core/Jamfile
@@ -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
diff --git a/test/beast/core/detail_read.cpp b/test/beast/core/detail_read.cpp
new file mode 100644
index 00000000..17f27f8c
--- /dev/null
+++ b/test/beast/core/detail_read.cpp
@@ -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
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+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
diff --git a/test/beast/http/read.cpp b/test/beast/http/read.cpp
index 2164d434..83a226ef 100644
--- a/test/beast/http/read.cpp
+++ b/test/beast/http/read.cpp
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
#include
namespace boost {