diff --git a/include/boost/beast/core/buffers_cat.hpp b/include/boost/beast/core/buffers_cat.hpp index f13732916b..50364e1c84 100644 --- a/include/boost/beast/core/buffers_cat.hpp +++ b/include/boost/beast/core/buffers_cat.hpp @@ -63,6 +63,13 @@ class buffers_cat_view /// Returns an iterator to one past the last buffer in the sequence const_iterator end() const; + + /// Get an element in the buffer sequence tuple. + template + const detail::tuple_element> & get() const + { + return detail::get(bn_); + } }; /** Concatenate 1 or more buffer sequences. diff --git a/include/boost/beast/core/buffers_suffix.hpp b/include/boost/beast/core/buffers_suffix.hpp index adf8fd5977..e7983bffe4 100644 --- a/include/boost/beast/core/buffers_suffix.hpp +++ b/include/boost/beast/core/buffers_suffix.hpp @@ -83,6 +83,9 @@ class buffers_suffix using value_type = buffers_type; #endif + /// The type of the underlying buffer sequence + using sequence_type = BufferSequence; + #if BOOST_BEAST_DOXYGEN /// A bidirectional iterator type that may be used to read elements. using const_iterator = __implementation_defined__; @@ -136,6 +139,12 @@ class buffers_suffix */ void consume(std::size_t amount); + + /// Get the underlying BufferSequence + const sequence_type & buffer_sequence() const {return bs_;} + + /// Get the amount of data to skipped at the beginning of the sequence. + std::size_t skip() const {return skip_;} }; } // beast diff --git a/include/boost/beast/http/impl/serializer.hpp b/include/boost/beast/http/impl/serializer.hpp index 39efb482d0..e28394ffee 100644 --- a/include/boost/beast/http/impl/serializer.hpp +++ b/include/boost/beast/http/impl/serializer.hpp @@ -285,26 +285,39 @@ next(error_code& ec, Visit&& visit) template< bool isRequest, class Body, class Fields> -void +std::size_t serializer:: consume(std::size_t n) { + std::size_t consumed = 0u; switch(s_) { case do_header: + { BOOST_ASSERT( n <= buffer_bytes(v_.template get<2>())); - v_.template get<2>().consume(n); - if(buffer_bytes(v_.template get<2>()) > 0) + cb2_t & cb = v_.template get<2>(); + + const std::size_t pre_size = buffer_bytes(cb.buffer_sequence().template get<0u>()); + const std::size_t skip = (std::max)(cb.skip(), pre_size); // the start of the consumed data + cb.consume(n); + if (cb.skip() > skip) + consumed += cb.skip() - skip; + + if (cb.skip() > pre_size) + header_done_ = true; + + if(buffer_bytes(cb) > 0) break; - header_done_ = true; + v_.reset(); if(! more_) goto go_complete; s_ = do_body + 1; break; - + } case do_header_only: + { BOOST_ASSERT( n <= buffer_bytes(v_.template get<1>())); v_.template get<1>().consume(n); @@ -316,12 +329,13 @@ consume(std::size_t n) goto go_complete; s_ = do_body; break; - + } case do_body + 2: { BOOST_ASSERT( n <= buffer_bytes(v_.template get<3>())); v_.template get<3>().consume(n); + consumed = n; if(buffer_bytes(v_.template get<3>()) > 0) break; v_.reset(); @@ -330,23 +344,43 @@ consume(std::size_t n) s_ = do_body + 1; break; } - //---------------------------------------------------------------------- case do_header_c: + { BOOST_ASSERT( n <= buffer_bytes(v_.template get<4>())); - v_.template get<4>().consume(n); - if(buffer_bytes(v_.template get<4>()) > 0) + + cb4_t& cb = v_.template get<4>(); + const typename cb4_t::sequence_type & bs = cb.buffer_sequence(); + + const std::size_t prefix = buffer_bytes(bs.template get<0>()) + + buffer_bytes(bs.template get<1>()) + + buffer_bytes(bs.template get<2>()) + + buffer_bytes(bs.template get<3>()); + + const std::size_t suffix = buffer_bytes(bs.template get<5>()); + const std::size_t skip = (std::max)(cb.skip(), prefix); + + cb.consume(n); + + const std::size_t consume_end = (std::min)(buffer_bytes(bs) - suffix, cb.skip()); + + if (cb.skip() > skip) + consumed += consume_end - skip; + + if (cb.skip() >= buffer_bytes(bs.template get<0>())) + header_done_ = true; + + if(buffer_bytes(cb) > 0) break; - header_done_ = true; v_.reset(); if(more_) s_ = do_body_c + 1; else s_ = do_final_c; break; - + } case do_header_only_c: { BOOST_ASSERT( @@ -366,10 +400,27 @@ consume(std::size_t n) } case do_body_c + 2: + { BOOST_ASSERT( n <= buffer_bytes(v_.template get<5>())); - v_.template get<5>().consume(n); - if(buffer_bytes(v_.template get<5>()) > 0) + + cb5_t& cb = v_.template get<5>(); + const typename cb5_t::sequence_type & bs = cb.buffer_sequence(); + + const std::size_t prefix = buffer_bytes(bs.template get<0>()) + + buffer_bytes(bs.template get<1>()) + + buffer_bytes(bs.template get<2>()); + + const std::size_t suffix = buffer_bytes(bs.template get<4>()); + const std::size_t skip = (std::max)(cb.skip(), prefix); + + cb.consume(n); + + const std::size_t consume_end = (std::min)({buffer_bytes(bs), suffix, cb.skip()}); + if (cb.skip() > skip) + consumed += consume_end - skip; + + if(buffer_bytes(cb) > 0) break; v_.reset(); if(more_) @@ -378,12 +429,33 @@ consume(std::size_t n) s_ = do_final_c; break; + } case do_body_final_c: { BOOST_ASSERT( n <= buffer_bytes(v_.template get<6>())); - v_.template get<6>().consume(n); - if(buffer_bytes(v_.template get<6>()) > 0) + + cb6_t& cb = v_.template get<6>(); + const typename cb6_t::sequence_type & bs = cb.buffer_sequence(); + + const std::size_t prefix = buffer_bytes(bs.template get<0>()) + + buffer_bytes(bs.template get<1>()) + + buffer_bytes(bs.template get<2>()); + + const std::size_t suffix = buffer_bytes(bs.template get<4>()) + + buffer_bytes(bs.template get<5>()) + + buffer_bytes(bs.template get<6>()) + + buffer_bytes(bs.template get<7>()); + + const std::size_t skip = (std::max)(cb.skip(), prefix); + + cb.consume(n); + + const std::size_t consume_end = (std::min)({buffer_bytes(bs), suffix, cb.skip()}); + if (cb.skip() > skip) + consumed += consume_end - skip; + + if(buffer_bytes(cb) > 0) break; v_.reset(); s_ = do_complete; @@ -394,10 +466,34 @@ consume(std::size_t n) { BOOST_ASSERT( n <= buffer_bytes(v_.template get<7>())); - v_.template get<7>().consume(n); - if(buffer_bytes(v_.template get<7>()) > 0) + + cb7_t& cb = v_.template get<7>(); + const typename cb7_t::sequence_type & bs = cb.buffer_sequence(); + + const std::size_t prefix = buffer_bytes(bs.template get<0>()) + + buffer_bytes(bs.template get<1>()) + + buffer_bytes(bs.template get<2>()) + + buffer_bytes(bs.template get<3>()); + + const std::size_t suffix = buffer_bytes(bs.template get<5>()) + + buffer_bytes(bs.template get<6>()) + + buffer_bytes(bs.template get<7>()) + + buffer_bytes(bs.template get<8>()); + + const std::size_t skip = (std::max)(cb.skip(), prefix); + + cb.consume(n); + + const std::size_t consume_end = (std::min)({buffer_bytes(bs), suffix, cb.skip()}); + if (cb.skip() > skip) + consumed += consume_end - skip; + + if (cb.skip() >= buffer_bytes(bs.template get<0>())) + header_done_ = true; + + if(buffer_bytes(cb) > 0) break; - header_done_ = true; + v_.reset(); s_ = do_complete; break; @@ -422,6 +518,7 @@ consume(std::size_t n) s_ = do_complete; break; } + return consumed; } } // http diff --git a/include/boost/beast/http/impl/write.hpp b/include/boost/beast/http/impl/write.hpp index 1e6a132d1f..1d88958b94 100644 --- a/include/boost/beast/http/impl/write.hpp +++ b/include/boost/beast/http/impl/write.hpp @@ -127,9 +127,10 @@ class write_some_op error_code ec, std::size_t bytes_transferred) { + std::size_t bytes_consumed = 0u; if(! ec) - sr_.consume(bytes_transferred); - this->complete_now(ec, bytes_transferred); + bytes_consumed = sr_.consume(bytes_transferred); + this->complete_now(ec, bytes_consumed); } }; @@ -511,15 +512,16 @@ write_some_impl( serializer& sr, error_code& ec) { + std::size_t consumed = 0u; if(! sr.is_done()) { write_some_lambda f{stream}; sr.next(ec, f); if(ec) - return f.bytes_transferred; + return consumed; if(f.invoked) - sr.consume(f.bytes_transferred); - return f.bytes_transferred; + consumed = sr.consume(f.bytes_transferred); + return consumed; } ec = {}; return 0; diff --git a/include/boost/beast/http/serializer.hpp b/include/boost/beast/http/serializer.hpp index 229554ddb0..8fe03cb313 100644 --- a/include/boost/beast/http/serializer.hpp +++ b/include/boost/beast/http/serializer.hpp @@ -356,8 +356,10 @@ class serializer @param n The number of octets to consume. This number must be greater than zero and no greater than the number of octets in the buffers provided in the prior call to @ref next. + + @return The amount of octets that were consumed from the previous call of .get on the body. */ - void + std::size_t consume(std::size_t n); /** Provides low-level access to the associated BodyWriter diff --git a/include/boost/beast/http/write.hpp b/include/boost/beast/http/write.hpp index fd82a667ef..174ae5b289 100644 --- a/include/boost/beast/http/write.hpp +++ b/include/boost/beast/http/write.hpp @@ -56,7 +56,7 @@ namespace http { @param sr The serializer to use. - @return The number of bytes written to the stream. + @return The number of bytes read from the body. @throws system_error Thrown on failure. @@ -100,7 +100,7 @@ write_some( @param ec Set to indicate what error occurred, if any. - @return The number of bytes written to the stream. + @return The number of bytes read from the body. @see async_write_some, serializer */ @@ -332,7 +332,6 @@ async_write_header( executor_type>{}); //------------------------------------------------------------------------------ - /** Write a complete message to a stream using a serializer. This function is used to write a complete message to a stream using @@ -351,7 +350,7 @@ async_write_header( @param sr The serializer to use. - @return The number of bytes written to the stream. + @return The number of bytes read from the body. @throws system_error Thrown on failure. @@ -385,7 +384,7 @@ write( @param ec Set to the error, if any occurred. - @return The number of bytes written to the stream. + @return The number of bytes read from the body. @see serializer */ @@ -428,7 +427,7 @@ write( @code void handler( error_code const& error, // result of operation - std::size_t bytes_transferred // the number of bytes written to the stream + std::size_t bytes_transferred // the number of bytes consumed from the body ); @endcode If the handler has an associated immediate executor, @@ -487,7 +486,7 @@ async_write( @param msg The message to write. - @return The number of bytes written to the stream. + @return The number of bytes read from the body. @throws system_error Thrown on failure. @@ -528,7 +527,7 @@ write( @param msg The message to write. - @return The number of bytes written to the stream. + @return The number of bytes read from the body. @throws system_error Thrown on failure. @@ -571,7 +570,7 @@ write( @param ec Set to the error, if any occurred. - @return The number of bytes written to the stream. + @return The number of bytes consumed from the body. @see message */ @@ -613,7 +612,7 @@ write( @param ec Set to the error, if any occurred. - @return The number of bytes written to the stream. + @return The number of bytes consumed from the body. @see message */ @@ -665,7 +664,7 @@ write( @code void handler( error_code const& error, // result of operation - std::size_t bytes_transferred // the number of bytes written to the stream + std::size_t bytes_transferred // the number of bytes consumed from the body ); @endcode If the handler has an associated immediate executor, @@ -739,7 +738,7 @@ async_write( @code void handler( error_code const& error, // result of operation - std::size_t bytes_transferred // the number of bytes written to the stream + std::size_t bytes_transferred // the number of bytes read from the body ); @endcode If the handler has an associated immediate executor, diff --git a/test/beast/http/serializer.cpp b/test/beast/http/serializer.cpp index 4b14d63fc7..270309a807 100644 --- a/test/beast/http/serializer.cpp +++ b/test/beast/http/serializer.cpp @@ -106,11 +106,19 @@ class serializer_test : public beast::unit_test::suite res.body().append(1000, '*'); serializer sr{res}; sr.limit(limit); + for(;;) { sr.next(ec, visit); BEAST_EXPECT(visit.size <= limit); - sr.consume(visit.size); + const bool header_was_done = sr.is_header_done(); + const auto n = sr.consume(visit.size); + + if (header_was_done && !sr.is_done()) + BEAST_EXPECT(n == limit); + else + BEAST_EXPECT(n <= limit); + if(sr.is_done()) break; }