From 731164bec2664ca57d1c167531695e59f9504e88 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Thu, 6 Jul 2017 20:58:07 -0700 Subject: [PATCH] Add serializer::limit --- CHANGELOG.md | 1 + include/beast/core/buffer_prefix.hpp | 6 +- include/beast/core/impl/buffer_prefix.ipp | 5 +- include/beast/http/impl/serializer.ipp | 107 ++++++++++++---------- include/beast/http/impl/write.ipp | 16 ++-- include/beast/http/serializer.hpp | 68 +++++++++++--- test/http/serializer.cpp | 54 +++++++++++ 7 files changed, 182 insertions(+), 75 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b6ba677..4761060e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Version 75: * Shrink serializer buffers using buffers_ref * Tidy up BEAST_NO_BIG_VARIANTS * Shrink serializer buffers using buffers_ref +* Add serializer::limit -------------------------------------------------------------------------------- diff --git a/include/beast/core/buffer_prefix.hpp b/include/beast/core/buffer_prefix.hpp index 6625b67e..1450504f 100644 --- a/include/beast/core/buffer_prefix.hpp +++ b/include/beast/core/buffer_prefix.hpp @@ -28,8 +28,10 @@ namespace beast { template class buffer_prefix_view { - using iter_type = - typename BufferSequence::const_iterator; + using buffers_type = typename + std::decay::type; + + using iter_type = typename buffers_type::const_iterator; BufferSequence bs_; iter_type back_; diff --git a/include/beast/core/impl/buffer_prefix.ipp b/include/beast/core/impl/buffer_prefix.ipp index f6c6470e..b3aa5578 100644 --- a/include/beast/core/impl/buffer_prefix.ipp +++ b/include/beast/core/impl/buffer_prefix.ipp @@ -48,11 +48,8 @@ class buffer_prefix_view::const_iterator { friend class buffer_prefix_view; - using iter_type = - typename BufferSequence::const_iterator; - buffer_prefix_view const* b_ = nullptr; - typename BufferSequence::const_iterator it_; + iter_type it_; public: using value_type = typename std::conditional< diff --git a/include/beast/http/impl/serializer.ipp b/include/beast/http/impl/serializer.ipp index 17618edc..4173b2c6 100644 --- a/include/beast/http/impl/serializer.ipp +++ b/include/beast/http/impl/serializer.ipp @@ -36,6 +36,22 @@ frdinit(std::false_type) frd_.emplace(m_, m_.version, m_.result_int()); } +template +template +inline +void +serializer:: +do_visit(error_code& ec, Visit& visit) +{ + // VFALCO work-around for missing variant::emplace + pv_.~variant(); + new(&pv_) decltype(pv_){ + T1{limit_, boost::get(v_)}}; + visit(ec, beast::detail::make_buffers_ref( + boost::get(pv_))); +} + template serializer:: @@ -83,7 +99,7 @@ next(error_code& ec, Visit&& visit) if(! result) goto go_header_only; more_ = result->second; - v_ = cb0_t{ + v_ = cb2_t{ boost::in_place_init, frd_->get(), result->first}; @@ -92,16 +108,14 @@ next(error_code& ec, Visit&& visit) } case do_header: - visit(ec, make_buffers_ref( - boost::get(v_))); + do_visit(ec, visit); break; go_header_only: - v_ = ch_t{frd_->get()}; + v_ = cb1_t{frd_->get()}; s_ = do_header_only; case do_header_only: - visit(ec, make_buffers_ref( - boost::get(v_))); + do_visit(ec, visit); break; case do_body: @@ -122,14 +136,13 @@ next(error_code& ec, Visit&& visit) if(! result) goto go_complete; more_ = result->second; - v_ = cb1_t{result->first}; + v_ = cb3_t{result->first}; s_ = do_body + 2; BEAST_FALLTHROUGH; } case do_body + 2: - visit(ec, make_buffers_ref( - boost::get(v_))); + do_visit(ec, visit); break; //---------------------------------------------------------------------- @@ -155,7 +168,7 @@ next(error_code& ec, Visit&& visit) if(! more_) { // do it all in one buffer - v_ = ch3_t{ + v_ = cb7_t{ boost::in_place_init, frd_->get(), detail::chunk_header{ @@ -183,7 +196,7 @@ next(error_code& ec, Visit&& visit) goto go_all_c; } #endif - v_ = ch0_t{ + v_ = cb4_t{ boost::in_place_init, frd_->get(), detail::chunk_header{ @@ -203,16 +216,14 @@ next(error_code& ec, Visit&& visit) } case do_header_c: - visit(ec, make_buffers_ref( - boost::get(v_))); + do_visit(ec, visit); break; go_header_only_c: - v_ = ch_t{frd_->get()}; + v_ = cb1_t{frd_->get()}; s_ = do_header_only_c; case do_header_only_c: - visit(ec, make_buffers_ref( - boost::get(v_))); + do_visit(ec, visit); break; case do_body_c: @@ -237,7 +248,7 @@ next(error_code& ec, Visit&& visit) if(! more_) { // do it all in one buffer - v_ = ch2_t{ + v_ = cb6_t{ boost::in_place_init, detail::chunk_header{ buffer_size(result->first)}, @@ -264,7 +275,7 @@ next(error_code& ec, Visit&& visit) goto go_body_final_c; } #endif - v_ = ch1_t{ + v_ = cb5_t{ boost::in_place_init, detail::chunk_header{ buffer_size(result->first)}, @@ -283,29 +294,26 @@ next(error_code& ec, Visit&& visit) } case do_body_c + 2: - visit(ec, make_buffers_ref( - boost::get(v_))); + do_visit(ec, visit); break; #ifndef BEAST_NO_BIG_VARIANTS go_body_final_c: s_ = do_body_final_c; case do_body_final_c: - visit(ec, make_buffers_ref( - boost::get(v_))); + do_visit(ec, visit); break; go_all_c: s_ = do_all_c; case do_all_c: - visit(ec, make_buffers_ref( - boost::get(v_))); + do_visit(ec, visit); break; #endif go_final_c: case do_final_c: - v_ = ch4_t{ + v_ = cb8_t{ boost::in_place_init, detail::chunk_final(), [&]() @@ -321,8 +329,7 @@ next(error_code& ec, Visit&& visit) BEAST_FALLTHROUGH; case do_final_c + 1: - visit(ec, make_buffers_ref( - boost::get(v_))); + do_visit(ec, visit); break; //---------------------------------------------------------------------- @@ -349,9 +356,9 @@ consume(std::size_t n) { case do_header: BOOST_ASSERT(n <= buffer_size( - boost::get(v_))); - boost::get(v_).consume(n); - if(buffer_size(boost::get(v_)) > 0) + boost::get(v_))); + boost::get(v_).consume(n); + if(buffer_size(boost::get(v_)) > 0) break; header_done_ = true; v_ = boost::blank{}; @@ -362,9 +369,9 @@ consume(std::size_t n) case do_header_only: BOOST_ASSERT(n <= buffer_size( - boost::get(v_))); - boost::get(v_).consume(n); - if(buffer_size(boost::get(v_)) > 0) + boost::get(v_))); + boost::get(v_).consume(n); + if(buffer_size(boost::get(v_)) > 0) break; frd_ = boost::none; header_done_ = true; @@ -376,9 +383,9 @@ consume(std::size_t n) case do_body + 2: { BOOST_ASSERT(n <= buffer_size( - boost::get(v_))); - boost::get(v_).consume(n); - if(buffer_size(boost::get(v_)) > 0) + boost::get(v_))); + boost::get(v_).consume(n); + if(buffer_size(boost::get(v_)) > 0) break; v_ = boost::blank{}; if(! more_) @@ -391,9 +398,9 @@ consume(std::size_t n) case do_header_c: BOOST_ASSERT(n <= buffer_size( - boost::get(v_))); - boost::get(v_).consume(n); - if(buffer_size(boost::get(v_)) > 0) + boost::get(v_))); + boost::get(v_).consume(n); + if(buffer_size(boost::get(v_)) > 0) break; header_done_ = true; v_ = boost::blank{}; @@ -406,9 +413,9 @@ consume(std::size_t n) case do_header_only_c: { BOOST_ASSERT(n <= buffer_size( - boost::get(v_))); - boost::get(v_).consume(n); - if(buffer_size(boost::get(v_)) > 0) + boost::get(v_))); + boost::get(v_).consume(n); + if(buffer_size(boost::get(v_)) > 0) break; frd_ = boost::none; header_done_ = true; @@ -423,9 +430,9 @@ consume(std::size_t n) case do_body_c + 2: BOOST_ASSERT(n <= buffer_size( - boost::get(v_))); - boost::get(v_).consume(n); - if(buffer_size(boost::get(v_)) > 0) + boost::get(v_))); + boost::get(v_).consume(n); + if(buffer_size(boost::get(v_)) > 0) break; v_ = boost::blank{}; if(more_) @@ -437,7 +444,7 @@ consume(std::size_t n) #ifndef BEAST_NO_BIG_VARIANTS case do_body_final_c: { - auto& b = boost::get(v_); + auto& b = boost::get(v_); BOOST_ASSERT(n <= buffer_size(b)); b.consume(n); if(buffer_size(b) > 0) @@ -449,7 +456,7 @@ consume(std::size_t n) case do_all_c: { - auto& b = boost::get(v_); + auto& b = boost::get(v_); BOOST_ASSERT(n <= buffer_size(b)); b.consume(n); if(buffer_size(b) > 0) @@ -463,9 +470,9 @@ consume(std::size_t n) case do_final_c + 1: BOOST_ASSERT(buffer_size( - boost::get(v_))); - boost::get(v_).consume(n); - if(buffer_size(boost::get(v_)) > 0) + boost::get(v_))); + boost::get(v_).consume(n); + if(buffer_size(boost::get(v_)) > 0) break; v_ = boost::blank{}; goto go_complete; diff --git a/include/beast/http/impl/write.ipp b/include/beast/http/impl/write.ipp index 08a63a2b..8b33e2c5 100644 --- a/include/beast/http/impl/write.ipp +++ b/include/beast/http/impl/write.ipp @@ -54,12 +54,12 @@ class write_some_op template void operator()(error_code& ec, - ConstBufferSequence const& buffer) + ConstBufferSequence const& buffers) { ec.assign(0, ec.category()); invoked = true; return op_.s_.async_write_some( - buffer, std::move(op_)); + buffers, std::move(op_)); } }; @@ -211,12 +211,12 @@ class write_op template void operator()(error_code& ec, - ConstBufferSequence const& buffer) + ConstBufferSequence const& buffers) { ec.assign(0, ec.category()); invoked = true; return op_.s_.async_write_some( - buffer, std::move(op_)); + buffers, std::move(op_)); } }; @@ -464,11 +464,11 @@ public: template void operator()(error_code& ec, - ConstBufferSequence const& buffer) + ConstBufferSequence const& buffers) { invoked = true; bytes_transferred = - stream_.write_some(buffer, ec); + stream_.write_some(buffers, ec); } }; @@ -490,11 +490,11 @@ public: template void operator()(error_code& ec, - ConstBufferSequence const& buffer) + ConstBufferSequence const& buffers) { invoked = true; bytes_transferred = boost::asio::write( - stream_, buffer, ec); + stream_, buffers, ec); } }; diff --git a/include/beast/http/serializer.hpp b/include/beast/http/serializer.hpp index 0ba8f431..b37a829d 100644 --- a/include/beast/http/serializer.hpp +++ b/include/beast/http/serializer.hpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -157,35 +158,44 @@ class serializer void frdinit(std::true_type); void frdinit(std::false_type); + template + void + do_visit(error_code& ec, Visit& visit); + using reader = typename Body::reader; - using ch_t = consuming_buffers; // header + using pcb1_t = buffer_prefix_view; - using cb0_t = consuming_buffers>; // body + using pcb2_t = buffer_prefix_view; - using cb1_t = consuming_buffers< + using cb3_t = consuming_buffers< typename reader::const_buffers_type>; // body + using pcb3_t = buffer_prefix_view; - using ch0_t = consuming_buffers>; // crlf + using pcb4_t = buffer_prefix_view; - using ch1_t = consuming_buffers>; // crlf + using pcb5_t = buffer_prefix_view; #ifndef BEAST_NO_BIG_VARIANTS - using ch2_t = consuming_buffers>; // crlf + using pcb6_t = buffer_prefix_view; - using ch3_t = consuming_buffers>; // crlf + using pcb7_t = buffer_prefix_view; #endif - using ch4_t = consuming_buffers>; // crlf + using pcb8_t = buffer_prefix_view; message const& m_; boost::optional frd_; boost::optional rd_; boost::variant v_; + , cb8_t> v_; + boost::variant pv_; + std::size_t limit_ = + (std::numeric_limits::max)(); int s_ = do_construct; bool split_ = false; bool header_done_ = false; @@ -246,6 +267,31 @@ public: serializer(message const& msg, ChunkDecorator const& decorator = ChunkDecorator{}); + /// Returns the serialized buffer size limit + std::size_t + limit() const + { + return limit_; + } + + /** Set the serialized buffer size limit + + This function adjusts the limit on the maximum size of the + buffers passed to the visitor. The new size limit takes effect + in the following call to @ref next. + + The default is no buffer size limit. + + @param limit The new buffer size limit. If this number + is zero, the size limit is removed. + */ + void + limit(std::size_t limit) + { + limit_ = limit > 0 ? limit: + (std::numeric_limits::max)(); + } + /** Returns `true` if we will pause after writing the complete header. */ bool diff --git a/test/http/serializer.cpp b/test/http/serializer.cpp index 3fb95423..022a09cd 100644 --- a/test/http/serializer.cpp +++ b/test/http/serializer.cpp @@ -7,3 +7,57 @@ // Test that header file is self-contained. #include + +#include +#include + +namespace beast { +namespace http { + +class serializer_test : public beast::unit_test::suite +{ +public: + struct lambda + { + std::size_t size; + + template + void + operator()(error_code&, + ConstBufferSequence const& buffers) + { + size = boost::asio::buffer_size(buffers); + } + }; + + void + testWriteLimit() + { + auto const limit = 30; + lambda visit; + error_code ec; + response res; + res.body.append(1000, '*'); + serializer sr{res}; + sr.limit(limit); + for(;;) + { + sr.next(ec, visit); + BEAST_EXPECT(visit.size <= limit); + sr.consume(visit.size); + if(sr.is_done()) + break; + } + } + + void + run() override + { + testWriteLimit(); + } +}; + +BEAST_DEFINE_TESTSUITE(serializer,http,beast); + +} // http +} // beast