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 {