From b76ab4450ad77128047eab090ca2d3d1ae9e3293 Mon Sep 17 00:00:00 2001 From: Damian Jarek Date: Mon, 13 Nov 2017 23:34:14 +0100 Subject: [PATCH] New BodyReader and BodyWriter constructors (API Change): fix #884 * BodyReader and BodyWriter constructors now require the header and body elements to be passed as distinct header and value_type objects. This enables the composition of body types, for example: http::response> res; * The previous single-argument constructors are deprecated and will be removed in a subsequent version. Actions Required: * Change user-defined instances of BodyReader or BodyWriter constructor signatures to the two-argument form. OR * Define the macro BOOST_BEAST_ALLOW_DEPRECATED in the project (which will accept both the new and the deprecated signatures). --- doc/qbk/07_concepts/BodyReader.qbk | 20 ++--- doc/qbk/07_concepts/BodyWriter.qbk | 20 ++--- .../boost/beast/http/basic_dynamic_body.hpp | 10 +-- include/boost/beast/http/basic_file_body.hpp | 25 +++--- include/boost/beast/http/buffer_body.hpp | 9 +- .../boost/beast/http/detail/type_traits.hpp | 16 ++++ include/boost/beast/http/empty_body.hpp | 5 +- .../boost/beast/http/impl/file_body_win32.ipp | 9 +- include/boost/beast/http/impl/parser.ipp | 84 ++++++++++++++++++- include/boost/beast/http/impl/serializer.ipp | 24 +++++- include/boost/beast/http/parser.hpp | 33 ++++++++ include/boost/beast/http/serializer.hpp | 24 ++++-- include/boost/beast/http/span_body.hpp | 10 +-- include/boost/beast/http/string_body.hpp | 10 +-- include/boost/beast/http/type_traits.hpp | 23 ++++- include/boost/beast/http/vector_body.hpp | 10 +-- test/Jamfile | 1 + test/beast/CMakeLists.txt | 2 + test/beast/http/fields.cpp | 2 +- test/beast/http/parser.cpp | 43 ++++++++++ test/beast/http/serializer.cpp | 48 ++++++++++- test/beast/http/span_body.cpp | 4 +- test/beast/http/write.cpp | 15 ++-- test/doc/exemplars.cpp | 18 ++-- 24 files changed, 358 insertions(+), 107 deletions(-) diff --git a/doc/qbk/07_concepts/BodyReader.qbk b/doc/qbk/07_concepts/BodyReader.qbk index 5c0c9291..ecc85212 100644 --- a/doc/qbk/07_concepts/BodyReader.qbk +++ b/doc/qbk/07_concepts/BodyReader.qbk @@ -41,27 +41,25 @@ In this table: `std::is_same::value == true`. * `a` denotes a value of type `R`. * `b` is an object whose type meets the requirements of __ConstBufferSequence__ -* `m` denotes a value of type `message&` where - `std::is_same::value == true`. +* `h` denotes a value of type `header&`. +* `v` denotes a value of type `Body::value_type&`. * `n` is a value of type `boost::optional`. * `ec` is a value of type [link beast.ref.boost__beast__error_code `error_code&`]. [table Valid expressions [[Expression] [Type] [Semantics, Pre/Post-conditions]] [ - [`R{m};`] + [`R{h,v};`] [] [ - Constructible from `m`. The lifetime of `m` is guaranteed to - end no earlier than after the `R` is destroyed. The constructor - will be called after a complete header is stored in `m`, and - before parsing body octets for messages indicating that a body - is present The reader shall not access the contents of `m` before + Constructible from `h` and `v`. The lifetime of `h` and `v` + is guaranteed to end no earlier than after the `R` is destroyed. + The constructor will be called after a complete header is stored + in `h`, and before parsing body octets for messages indicating + that a body is present. + The reader shall not access the contents of `h` or `v` before the first call to `init`, permitting lazy construction of the message. - - The function will ensure that `!ec` is `true` if there was - no error or set to the appropriate error code if there was one. ] ][ [`a.init(n, ec)`] diff --git a/doc/qbk/07_concepts/BodyWriter.qbk b/doc/qbk/07_concepts/BodyWriter.qbk index f56222ca..578326da 100644 --- a/doc/qbk/07_concepts/BodyWriter.qbk +++ b/doc/qbk/07_concepts/BodyWriter.qbk @@ -42,8 +42,8 @@ In this table: * `B` denotes a __Body__ where `std::is_same::value == true`. * `a` denotes a value of type `W`. -* `m` denotes a possibly const value of type `message&` where - `std::is_same:value == true`. +* `h` denotes a const value of type `header const&`. +* `v` denotes a possibly const value of type `Body::value_type&`. * `ec` is a value of type [link beast.ref.boost__beast__error_code `error_code&`]. * `W` is the type `boost::optional>`. @@ -57,23 +57,23 @@ In this table: This is the type of buffer returned by `W::get`. ] ][ - [`W{m};`] + [`W{h,v};`] [] [ - Constructible from `m`. The lifetime of `m` is guaranteed - to end no earlier than after the `W` is destroyed. - The writer shall not access the contents of `m` before the - first call to `init`, permitting lazy construction of the + Constructible from `h` and `v`. The lifetime of `h` and `v` + is guaranteed to end no earlier than after the `W` is destroyed. + The writer shall not access the contents of `h` or `v` before + the first call to `init`, permitting lazy construction of the message. - The constructor may optionally require that `m` is const, which + The constructor may optionally require that `v` is const, which has these consequences: - * If `W` requires that `m` is a const reference, then serializers + * If `W` requires that `v` is a const reference, then serializers constructed for messages with this body type will also require a const reference to a message, otherwise: - * If `W` requires that `m` is a non-const reference, then serializers + * If `W` requires that `v` is a non-const reference, then serializers constructed for messages with this body type will aso require a non-const reference to a message. ] diff --git a/include/boost/beast/http/basic_dynamic_body.hpp b/include/boost/beast/http/basic_dynamic_body.hpp index 76c499c5..7d850d76 100644 --- a/include/boost/beast/http/basic_dynamic_body.hpp +++ b/include/boost/beast/http/basic_dynamic_body.hpp @@ -70,9 +70,8 @@ struct basic_dynamic_body public: template explicit - reader(message& msg) - : body_(msg.body()) + reader(header&, value_type& b) + : body_(b) { } @@ -140,9 +139,8 @@ struct basic_dynamic_body template explicit - writer(message const& m) - : body_(m.body()) + writer(header const&, value_type const& b) + : body_(b) { } diff --git a/include/boost/beast/http/basic_file_body.hpp b/include/boost/beast/http/basic_file_body.hpp index cec72a8f..6d1de9a3 100644 --- a/include/boost/beast/http/basic_file_body.hpp +++ b/include/boost/beast/http/basic_file_body.hpp @@ -239,8 +239,8 @@ public: // Constructor. // - // `m` holds the message we are serializing, which will - // always have the `basic_file_body` as the body type. + // `h` holds the headers of the message we are + // serializing, while `b` holds the body. // // Note that the message is passed by non-const reference. // This is intentional, because reading from the file @@ -262,8 +262,7 @@ public: // a time. // template - writer(message< - isRequest, basic_file_body, Fields>& m); + writer(header& h, value_type& b); // Initializer // @@ -296,9 +295,11 @@ template template basic_file_body:: writer:: -writer(message& m) - : body_(m.body()) +writer(header& h, value_type& b) + : body_(b) { + boost::ignore_unused(h); + // The file must already be open BOOST_ASSERT(body_.file_.is_open()); @@ -398,13 +399,12 @@ public: // // This is called after the header is parsed and // indicates that a non-zero sized body may be present. - // `m` holds the message we are receiving, which will - // always have the `basic_file_body` as the body type. + // `h` holds the received message headers. + // `b` is an instance of `basic_file_body`. // template explicit - reader( - message& m); + reader(header&h, value_type& b); // Initializer // @@ -447,9 +447,10 @@ template template basic_file_body:: reader:: -reader(message& m) - : body_(m.body()) +reader(header& h, value_type& body) + : body_(body) { + boost::ignore_unused(h); } template diff --git a/include/boost/beast/http/buffer_body.hpp b/include/boost/beast/http/buffer_body.hpp index 675ea947..3ffe0a00 100644 --- a/include/boost/beast/http/buffer_body.hpp +++ b/include/boost/beast/http/buffer_body.hpp @@ -105,8 +105,8 @@ struct buffer_body public: template explicit - reader(message& m) - : body_(m.body()) + reader(header&, value_type& b) + : body_(b) { } @@ -167,9 +167,8 @@ struct buffer_body template explicit - writer(message const& msg) - : body_(msg.body()) + writer(header const&, value_type const& b) + : body_(b) { } diff --git a/include/boost/beast/http/detail/type_traits.hpp b/include/boost/beast/http/detail/type_traits.hpp index c9a06f97..1d0c9918 100644 --- a/include/boost/beast/http/detail/type_traits.hpp +++ b/include/boost/beast/http/detail/type_traits.hpp @@ -194,6 +194,22 @@ struct is_fields_helper : T t10::value && t11::value && t12::value>; }; +template +using has_deprecated_body_writer = + std::integral_constant&>::value && + std::is_constructible&>::value>; + +template +using has_deprecated_body_reader = + std::integral_constant&>::value && + std::is_constructible&>::value>; + } // detail } // http } // beast diff --git a/include/boost/beast/http/empty_body.hpp b/include/boost/beast/http/empty_body.hpp index 1f1bf7fb..d56c14b4 100644 --- a/include/boost/beast/http/empty_body.hpp +++ b/include/boost/beast/http/empty_body.hpp @@ -63,7 +63,7 @@ struct empty_body { template explicit - reader(message&) + reader(header&, value_type&) { } @@ -104,8 +104,7 @@ struct empty_body template explicit - writer(message const&) + writer(header const&, value_type const&) { } diff --git a/include/boost/beast/http/impl/file_body_win32.ipp b/include/boost/beast/http/impl/file_body_win32.ipp index 1c748fe0..6d2a82ec 100644 --- a/include/boost/beast/http/impl/file_body_win32.ipp +++ b/include/boost/beast/http/impl/file_body_win32.ipp @@ -123,9 +123,8 @@ struct basic_file_body boost::asio::const_buffer; template - writer(message, Fields>& m) - : body_(m.body()) + writer(header&, value_type& b) + : body_(b) { } @@ -167,8 +166,8 @@ struct basic_file_body public: template explicit - reader(message& m) - : body_(m.body()) + reader(header&, value_type& b) + : body_(b) { } diff --git a/include/boost/beast/http/impl/parser.ipp b/include/boost/beast/http/impl/parser.ipp index 0fddf792..8a6cab30 100644 --- a/include/boost/beast/http/impl/parser.ipp +++ b/include/boost/beast/http/impl/parser.ipp @@ -20,26 +20,87 @@ namespace http { template parser:: parser() + : parser{detail::has_deprecated_body_reader{}} +{ +} + +template +parser:: +parser(std::true_type) : rd_(m_) { +#ifndef BOOST_BEAST_ALLOW_DEPRECATED + // Deprecated BodyReader Concept (v1.66) + static_assert(sizeof(Body) == 0, + BOOST_BEAST_DEPRECATION_STRING); +#endif +} + +template +parser:: +parser(std::false_type) + : rd_(m_.base(), m_.body()) +{ } template template parser:: parser(Arg1&& arg1, ArgN&&... argn) + : parser(std::forward(arg1), + detail::has_deprecated_body_reader{}, + std::forward(argn)...) +{ +} + +// VFALCO arg1 comes before `true_type` to make +// the signature unambiguous. +template +template +parser:: +parser(Arg1&& arg1, std::true_type, ArgN&&... argn) : m_(std::forward(arg1), std::forward(argn)...) , rd_(m_) { m_.clear(); +#ifndef BOOST_BEAST_ALLOW_DEPRECATED + /* Deprecated BodyWriter Concept (v1.66) */ + static_assert(sizeof(Body) == 0, + BOOST_BEAST_DEPRECATION_STRING); +#endif // BOOST_BEAST_ALLOW_DEPRECATED +} + +// VFALCO arg1 comes before `false_type` to make +// the signature unambiguous. +template +template +parser:: +parser(Arg1&& arg1, std::false_type, ArgN&&... argn) + : m_(std::forward(arg1), + std::forward(argn)...) + , rd_(m_.base(), m_.body()) +{ + m_.clear(); } template template parser:: -parser(parser&& other, - Args&&... args) +parser( + parser&& other, + Args&&... args) + : parser(detail::has_deprecated_body_reader{}, + std::move(other), std::forward(args)...) +{ +} + +template +template +parser:: +parser(std::true_type, + parser&& other, + Args&&... args) : base_type(std::move(other)) , m_(other.release(), std::forward(args)...) , rd_(m_) @@ -47,6 +108,25 @@ parser(parser&& other, if(other.rd_inited_) BOOST_THROW_EXCEPTION(std::invalid_argument{ "moved-from parser has a body"}); +#ifndef BOOST_BEAST_ALLOW_DEPRECATED + // Deprecated BodyReader Concept (v1.66) + static_assert(sizeof(Body) == 0, + BOOST_BEAST_DEPRECATION_STRING); +#endif +} + +template +template +parser:: +parser(std::false_type, parser&& other, + Args&&... args) + : base_type(std::move(other)) + , m_(other.release(), std::forward(args)...) + , rd_(m_.base(), m_.body()) +{ + if(other.rd_inited_) + BOOST_THROW_EXCEPTION(std::invalid_argument{ + "moved-from parser has a body"}); } } // http diff --git a/include/boost/beast/http/impl/serializer.ipp b/include/boost/beast/http/impl/serializer.ipp index 8ce9d8fc..6b1a2dd9 100644 --- a/include/boost/beast/http/impl/serializer.ipp +++ b/include/boost/beast/http/impl/serializer.ipp @@ -57,10 +57,32 @@ do_visit(error_code& ec, Visit& visit) template< bool isRequest, class Body, class Fields> serializer:: -serializer(value_type& m) +serializer(value_type& m, std::true_type) : m_(m) , wr_(m_) { +#ifndef BOOST_BEAST_ALLOW_DEPRECATED + // Deprecated BodyWriter Concept (v1.66) + static_assert(sizeof(Body) == 0, + BOOST_BEAST_DEPRECATION_STRING); +#endif +} + +template< + bool isRequest, class Body, class Fields> +serializer:: +serializer(value_type& m, std::false_type) + : m_(m) + , wr_(m_.base(), m_.body()) +{ +} + +template< + bool isRequest, class Body, class Fields> +serializer:: +serializer(value_type& m) + : serializer(m, detail::has_deprecated_body_writer{}) +{ } template< diff --git a/include/boost/beast/http/parser.hpp b/include/boost/beast/http/parser.hpp index b0799aac..17ed4969 100644 --- a/include/boost/beast/http/parser.hpp +++ b/include/boost/beast/http/parser.hpp @@ -303,6 +303,39 @@ public: private: friend class basic_parser; + parser(std::true_type); + parser(std::false_type); + + template::value>::type> + parser( + std::true_type, + parser&& parser, + Args&&... args); + + template::value>::type> + parser( + std::false_type, + parser&& parser, + Args&&... args); + + template::type>::value>::type> + explicit + parser(Arg1&& arg1, std::true_type, ArgN&&... argn); + + template::type>::value>::type> + explicit + parser(Arg1&& arg1, std::false_type, ArgN&&... argn); + void on_request_impl( verb method, diff --git a/include/boost/beast/http/serializer.hpp b/include/boost/beast/http/serializer.hpp index 640476b8..bbb562a6 100644 --- a/include/boost/beast/http/serializer.hpp +++ b/include/boost/beast/http/serializer.hpp @@ -71,14 +71,20 @@ public: #if BOOST_BEAST_DOXYGEN using value_type = implementation_defined; #else - using value_type = - typename std::conditional< - std::is_constructible&>::value && - ! std::is_constructible const&>::value, - message, - message const>::type; + using value_type = typename std::conditional< + (std::is_constructible&, + typename Body::value_type&>::value && + ! std::is_constructible const&, + typename Body::value_type const&>::value) || + // Deprecated BodyWriter Concept (v1.66) + (std::is_constructible&>::value && + ! std::is_constructible const&>::value), + message, + message const>::type; #endif private: @@ -188,6 +194,8 @@ private: bool header_done_ = false; bool more_; + serializer(value_type& msg, std::true_type); + serializer(value_type& msg, std::false_type); public: /// Constructor serializer(serializer&&) = default; diff --git a/include/boost/beast/http/span_body.hpp b/include/boost/beast/http/span_body.hpp index 6282ecc6..4688dc87 100644 --- a/include/boost/beast/http/span_body.hpp +++ b/include/boost/beast/http/span_body.hpp @@ -73,9 +73,8 @@ public: public: template explicit - reader(message& m) - : body_(m.body()) + reader(header&, value_type& b) + : body_(b) { } @@ -138,9 +137,8 @@ public: template explicit - writer(message const& msg) - : body_(msg.body()) + writer(header const&, value_type const& b) + : body_(b) { } diff --git a/include/boost/beast/http/string_body.hpp b/include/boost/beast/http/string_body.hpp index 18eda414..89447e50 100644 --- a/include/boost/beast/http/string_body.hpp +++ b/include/boost/beast/http/string_body.hpp @@ -81,9 +81,8 @@ public: public: template explicit - reader(message& m) - : body_(m.body()) + reader(header&, value_type& b) + : body_(b) { } @@ -166,9 +165,8 @@ public: template explicit - writer(message const& msg) - : body_(msg.body()) + writer(header const&, value_type const& b) + : body_(b) { } diff --git a/include/boost/beast/http/type_traits.hpp b/include/boost/beast/http/type_traits.hpp index f90f16ff..0c821044 100644 --- a/include/boost/beast/http/type_traits.hpp +++ b/include/boost/beast/http/type_traits.hpp @@ -89,11 +89,19 @@ struct is_body_writer().get(std::declval()), (void)0)>> : std::integral_constant::value && + typename T::writer::const_buffers_type>::value && ( + (std::is_constructible&, + typename T::value_type&>::value && std::is_constructible&, + typename T::value_type&>::value) || + // Deprecated BodyWriter Concept (v1.66) + (std::is_constructible&>::value && std::is_constructible&>::value + message&>::value) + ) > {}; #endif @@ -136,11 +144,18 @@ struct is_body_reader().finish( std::declval()), (void)0)>> : std::integral_constant&, + typename T::value_type&>::value && std::is_constructible&, + typename T::value_type&>::value) || + // Deprecated BodyReader Concept (v1.66) + (std::is_constructible&>::value && std::is_constructible&>::value - > + message&>::value) + > { }; #endif diff --git a/include/boost/beast/http/vector_body.hpp b/include/boost/beast/http/vector_body.hpp index eb37145d..40c885e4 100644 --- a/include/boost/beast/http/vector_body.hpp +++ b/include/boost/beast/http/vector_body.hpp @@ -76,9 +76,8 @@ public: public: template explicit - reader(message& m) - : body_(m.body()) + reader(header&, value_type& b) + : body_(b) { } @@ -155,9 +154,8 @@ public: template explicit - writer(message const& msg) - : body_(msg.body()) + writer(header const&, value_type const& b) + : body_(b) { } diff --git a/test/Jamfile b/test/Jamfile index cdaa6513..3c7a86e5 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -21,6 +21,7 @@ project /boost/beast/test cxx11_variadic_templates ] ./extern + BOOST_BEAST_ALLOW_DEPRECATED ; path-constant ZLIB_SOURCES : diff --git a/test/beast/CMakeLists.txt b/test/beast/CMakeLists.txt index 5ee064d4..f6d1f5eb 100644 --- a/test/beast/CMakeLists.txt +++ b/test/beast/CMakeLists.txt @@ -7,6 +7,8 @@ # Official repository: https://github.com/boostorg/beast # +add_definitions (-DBOOST_BEAST_ALLOW_DEPRECATED) + GroupSources(include/boost/beast beast) GroupSources(test/extras/include/boost/beast extras) GroupSources(subtree/unit_test/include/boost/beast extras) diff --git a/test/beast/http/fields.cpp b/test/beast/http/fields.cpp index 7897ed59..38ae5386 100644 --- a/test/beast/http/fields.cpp +++ b/test/beast/http/fields.cpp @@ -48,7 +48,7 @@ public: ::operator delete(p); } - template + template friend bool operator==(test_allocator const&, test_allocator const&) noexcept diff --git a/test/beast/http/parser.cpp b/test/beast/http/parser.cpp index 59374565..763c9c85 100644 --- a/test/beast/http/parser.cpp +++ b/test/beast/http/parser.cpp @@ -36,6 +36,42 @@ public: using parser_type = parser; + struct deprecated_body + { + using value_type = std::string; + + class reader + { + public: + template + explicit + reader(message&) + { + } + + void + init(boost::optional const&, error_code& ec) + { + ec = {}; + } + + template + std::size_t + put(ConstBufferSequence const& buffers, error_code& ec) + { + ec = {}; + return boost::asio::buffer_size(buffers); + } + + void + finish(error_code& ec) + { + ec = {}; + } + }; + }; + + static boost::asio::const_buffer buf(string_view s) @@ -343,6 +379,12 @@ public: BEAST_EXPECT(std::distance(m1.begin(), m1.end()) == 0); } + void testBodyReaderCtor() + { + request_parser p; + boost::ignore_unused(p); + } + void run() override { @@ -351,6 +393,7 @@ public: testNeedMore(); testGotSome(); testIssue818(); + testBodyReaderCtor(); } }; diff --git a/test/beast/http/serializer.cpp b/test/beast/http/serializer.cpp index d4cab4ad..70405e83 100644 --- a/test/beast/http/serializer.cpp +++ b/test/beast/http/serializer.cpp @@ -20,6 +20,40 @@ namespace http { class serializer_test : public beast::unit_test::suite { public: + struct deprecated_body + { + using value_type = std::string; + + class writer + { + public: + using const_buffers_type = + boost::asio::const_buffer; + + value_type const& body_; + + template + explicit + writer(message const& m): + body_{m.body()} + { + } + + void init(error_code& ec) + { + ec.assign(0, ec.category()); + } + + boost::optional> + get(error_code& ec) + { + ec.assign(0, ec.category()); + return {{const_buffers_type{ + body_.data(), body_.size()}, false}}; + } + }; + }; + struct const_body { struct value_type{}; @@ -30,7 +64,7 @@ public: boost::asio::const_buffer; template - writer(message const&); + writer(header const&, value_type const&); void init(error_code& ec); @@ -50,7 +84,7 @@ public: boost::asio::const_buffer; template - writer(message&); + writer(header&, value_type&); void init(error_code& ec); @@ -115,10 +149,20 @@ public: } } + void testBodyWriterCtor() + { + response res; + request req; + serializer sr1{res}; + serializer sr2{req}; + boost::ignore_unused(sr1, sr2); + } + void run() override { testWriteLimit(); + testBodyWriterCtor(); } }; diff --git a/test/beast/http/span_body.cpp b/test/beast/http/span_body.cpp index d76ddd06..24e5da3a 100644 --- a/test/beast/http/span_body.cpp +++ b/test/beast/http/span_body.cpp @@ -34,7 +34,7 @@ struct span_body_test BEAST_EXPECT(req.body().size() == 3); BEAST_EXPECT(B::size(req.body()) == 3); - B::writer r{req}; + B::writer r{req, req.body()}; error_code ec; r.init(ec); BEAST_EXPECTS(! ec, ec.message()); @@ -50,7 +50,7 @@ struct span_body_test using B = span_body; request req; req.body() = span{buf, sizeof(buf)}; - B::reader w{req}; + B::reader w{req, req.body()}; error_code ec; w.init(boost::none, ec); BEAST_EXPECTS(! ec, ec.message()); diff --git a/test/beast/http/write.cpp b/test/beast/http/write.cpp index 5ac2993b..2b81ff22 100644 --- a/test/beast/http/write.cpp +++ b/test/beast/http/write.cpp @@ -48,9 +48,8 @@ public: template explicit - writer(message const& msg) - : body_(msg.body()) + writer(header const&, value_type const& b) + : body_(b) { } @@ -93,9 +92,8 @@ public: template explicit - writer(message const& msg) - : body_(msg.body()) + writer(header const&, value_type const& b) + : body_(b) { } @@ -230,9 +228,8 @@ public: template explicit - writer(message const& msg) - : body_(msg.body()) + writer(header const&, value_type const& b) + : body_(b) { } diff --git a/test/doc/exemplars.cpp b/test/doc/exemplars.cpp index 5e8f281d..d45d36aa 100644 --- a/test/doc/exemplars.cpp +++ b/test/doc/exemplars.cpp @@ -57,11 +57,12 @@ public: /** Construct the writer. - @param msg The message whose body is to be serialized. + @param h The header for the message being serialized + + @param body The body being serialized */ - template - explicit - BodyWriter(message const& msg); + template + BodyWriter(header const& h, value_type const& body); /** Initialize the writer. @@ -120,11 +121,12 @@ struct BodyReader { /** Construct the reader. - @param msg The message whose body is to be parsed. + @param h The header for the message being parsed + + @param body The body to store the parsed results into */ - template - explicit - BodyReader(message& msg); + template + BodyReader(header& h, value_type& body); /** Initialize the reader.