diff --git a/CHANGELOG.md b/CHANGELOG.md index 97ee16aa..f11864d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ * Fix basic_streambuf::capacity * Add basic_streambuf::alloc_size * Parser callbacks may not throw +* Fix Reader concept doc typo + +API Changes: + +* Added init() to Reader requirements +* Reader must be nothrow constructible +* Reader is now constructed right before reading the body + - The message passed on construction is filled in -------------------------------------------------------------------------------- diff --git a/doc/types/Reader.qbk b/doc/types/Reader.qbk index 20059c31..d1952e42 100644 --- a/doc/types/Reader.qbk +++ b/doc/types/Reader.qbk @@ -8,23 +8,23 @@ [section:Reader Reader requirements] Parsers provided by the implementation will construct the corresponding -`reader` object during the parse. This customization point allows the +`reader` object during parsing. This customization point allows the Body to determine the strategy for storing incoming message body data. In this table: -* `X` denotes a type meeting the requirements of [*`Reader`] +* `X` denotes a type meeting the requirements of [*`Reader`]. -* `a` denotes a value of type `X` +* `a` denotes a value of type `X`. -* `p` is any pointer +* `n` is a value convertible to `std::size_t`. -* `n` is a value convertible to `std::size_t` +* `p` is a `void const*` to valid memory of at least `n` bytes. -* `ec` is a value of type [link beast.ref.error_code `error_code&`] +* `ec` is a value of type [link beast.ref.error_code `error_code&`]. -* `m` denotes a value of type `message const&` where - `std::is_same::value == true` +* `m` denotes a value of type `message&` where + `std::is_same::value == true`. [table Reader requirements [[operation] [type] [semantics, pre/post-conditions]] @@ -32,22 +32,38 @@ In this table: [`X a(m);`] [] [ - `a` is constructible from `m`. The lifetime of `m` is - guaranteed to end no earlier than after `a` is destroyed. + `a` is constructible from `m`. The lifetime of `m` is guaranteed + to end no earlier than after `a` is destroyed. The constructor + will be called after all headers have been stored in `m`, and + before any body data is deserialized. This function must be + `noexcept`. + ] +] +[ + [`a.init(ec)`] + [`void`] + [ + Called immediately after construction. If the function sets + an error code in `ec`, the parse is aborted and the error is + propagated to the caller. This function must be `noexcept`. ] ] [ [`a.write(p, n, ec)`] [`void`] [ - Deserializes the input sequence into the body. - If `ec` is set, the deserialization is aborted and the error - is returned to the caller. + Deserializes the input sequence into the body. If `ec` is set, + the deserialization is aborted and the error is propagated to + the caller. If the message headers specify a chunked transfer + encoding, the reader will receive the decoded version of the + body. This function must be `noexcept`. ] ] ] -[note Definitions for required `Reader` member functions should be declared -inline so the generated code becomes part of the implementation. ] +[note + Definitions for required `Reader` member functions should be declared + inline so the generated code can become part of the implementation. +] [endsect] diff --git a/doc/types/Writer.qbk b/doc/types/Writer.qbk index 56de5efb..48c91aee 100644 --- a/doc/types/Writer.qbk +++ b/doc/types/Writer.qbk @@ -28,13 +28,13 @@ In this table: * `m` denotes a value of type `message const&` where `std::is_same:value == true`. -* `rc` is an object of type [link beast.ref.http__resume_context resume_context]. +* `rc` is an object of type [link beast.ref.http__resume_context `resume_context`]. -* `ec` is a value of type `error_code&`. +* `ec` is a value of type [link beast.ref.error_code `error_code&`] * `wf` is a [*write function]: a function object of unspecified type provided by the implementation which accepts any value meeting the requirements - of `ConstBufferSequence` as its single parameter. + of __ConstBufferSequence__ as its single parameter. [table Writer requirements [[operation] [type] [semantics, pre/post-conditions]] @@ -42,17 +42,18 @@ In this table: [`X a(m);`] [] [ - `a` is constructible from `m`. The lifetime of `m` is - guaranteed to end no earlier than after `a` is destroyed. + `a` is constructible from `m`. The lifetime of `m` is guaranteed + to end no earlier than after `a` is destroyed. This function must + be `noexcept`. ] ] [ [`a.init(ec)`] [`void`] [ - Called immediately after construction. - If `ec` is set, the serialization is aborted and the error - is propagated to the caller. + Called immediately after construction. If the function sets an + error code in `ec`, the serialization is aborted and the error + is propagated to the caller. This function must be `noexcept`. ] ] [ @@ -67,31 +68,33 @@ In this table: the serialized message body will be sent unmodified, with the error `boost::asio::error::eof` returned to the caller, to notify they should close the connection to indicate the end of the message. + This function must be `noexcept`. ] ] [ [`a(rc, ec, wf)`] [`boost::tribool`] [ - Called repeatedly after `init` succeeds. - `wf` is a function object which takes as its single parameter, - any value meeting the requirements of `ConstBufferSequence`. - Buffers provided by the `writer` to this [*write function] must - remain valid until the next member function of `writer` is + Called repeatedly after `init` succeeds. `wf` is a function object + which takes as its single parameter any value meeting the requirements + of __ConstBufferSequence__. Buffers provided to this write function + must remain valid until the next member function of `writer` is invoked (which may be the destructor). This function returns `true` - to indicate all message body data has been written, or `false` - if there is more body data. If the return value is - `boost::indeterminate`, the implementation will suspend the operation - until the writer invokes `rc`. It is the writers responsibility when - returning `boost::indeterminate`, to acquire ownership of the - `resume_context` via move construction and eventually call it or else - undefined behavior results. + to indicate all message body data has been written, or `false` if + there is more body data. If the return value is `boost::indeterminate`, + the implementation will suspend the operation until the writer invokes + `rc`. It is the writers responsibility when returning + `boost::indeterminate`, to acquire ownership of `rc` via move + construction and eventually call it or else undefined behavior + results. This function must be `noexcept`. ] ] ] -[note Definitions for required `Writer` member functions should be declared -inline so the generated code becomes part of the implementation. ] +[note + Definitions for required `Writer` member functions should be declared + inline so the generated code can become part of the implementation. +] Exemplar: ``` @@ -109,7 +112,7 @@ public: */ template explicit - writer(message const& msg); + writer(message const& msg) noexcept; /** Initialize the writer. @@ -119,7 +122,7 @@ public: @param ec Contains the error code if any errors occur. */ void - init(error_code& ec); + init(error_code& ec) noexcept; /** Returns the content length. @@ -129,7 +132,7 @@ public: of the message. */ std::uint64_t - content_length(); + content_length() noexcept; /** Write zero or one buffer representing the message body. @@ -172,7 +175,10 @@ public: */ template boost::tribool - operator()(resume_context&&, error_code&, WriteFunction&& write); + operator()( + resume_context&&, + error_code&, + WriteFunction&& write) noexcept; }; ``` diff --git a/include/beast/http/basic_dynabuf_body.hpp b/include/beast/http/basic_dynabuf_body.hpp index ee84ca53..3a46aed0 100644 --- a/include/beast/http/basic_dynabuf_body.hpp +++ b/include/beast/http/basic_dynabuf_body.hpp @@ -41,6 +41,11 @@ private: { } + void + init(error_code&) noexcept + { + } + void write(void const* data, std::size_t size, error_code&) noexcept diff --git a/include/beast/http/basic_parser_v1.hpp b/include/beast/http/basic_parser_v1.hpp index 0df50283..86a13e1d 100644 --- a/include/beast/http/basic_parser_v1.hpp +++ b/include/beast/http/basic_parser_v1.hpp @@ -190,9 +190,9 @@ static std::uint64_t constexpr no_content_length = @li `void on_complete(error_code&)` Called when the entire message has been parsed successfully. - At this point, @ref basic_parser_v1::complete returns `true`, and - the parser is ready to parse another message if keep_alive - would return `true`. + At this point, @ref complete returns `true`, and the parser + is ready to parse another message if @ref keep_alive would + return `true`. The return value of `on_headers` is special, it controls whether or not the parser should expect a body. See @ref body_what for diff --git a/include/beast/http/parser_v1.hpp b/include/beast/http/parser_v1.hpp index 731d4595..dc1f4f99 100644 --- a/include/beast/http/parser_v1.hpp +++ b/include/beast/http/parser_v1.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -87,17 +88,28 @@ public: message_v1; private: + using reader = + typename message_type::body_type::reader; + static_assert(is_ReadableBody::value, "ReadableBody requirements not met"); + // Reader must be nothrow constructible + static_assert(std::is_nothrow_constructible< + reader, message_type&>::value, + "Reader requirements not met"); + std::string field_; std::string value_; message_type m_; - typename message_type::body_type::reader r_; + boost::optional r_; std::uint8_t skip_body_ = 0; bool flush_ = false; public: + /// Default constructor + parser_v1() = default; + /// Move constructor parser_v1(parser_v1&&) = default; @@ -110,12 +122,6 @@ public: /// Copy assignment (disallowed) parser_v1& operator=(parser_v1 const&) = delete; - /// Default constructor - parser_v1() - : r_(m_) - { - } - /** Construct the parser. @param args A list of arguments forwarded to the message constructor. @@ -127,7 +133,6 @@ public: parser_v1(Arg1&& arg1, ArgN&&... argn) : m_(std::forward(arg1), std::forward(argn)...) - , r_(m_) { } @@ -232,12 +237,14 @@ private: } body_what - on_headers(std::uint64_t, error_code&) + on_headers(std::uint64_t, error_code& ec) { flush(); m_.version = 10 * this->http_major() + this->http_minor(); if(skip_body_) return body_what::skip; + r_.emplace(m_); + r_->init(ec); return body_what::normal; } @@ -255,7 +262,7 @@ private: void on_body(boost::string_ref const& s, error_code& ec) { - r_.write(s.data(), s.size(), ec); + r_->write(s.data(), s.size(), ec); } void on_complete(error_code&) diff --git a/include/beast/http/string_body.hpp b/include/beast/http/string_body.hpp index 76e46a05..1148c67f 100644 --- a/include/beast/http/string_body.hpp +++ b/include/beast/http/string_body.hpp @@ -42,6 +42,11 @@ private: { } + void + init(error_code&) noexcept + { + } + void write(void const* data, std::size_t size, error_code&) noexcept diff --git a/test/http/read.cpp b/test/http/read.cpp index f3f1c327..074f5754 100644 --- a/test/http/read.cpp +++ b/test/http/read.cpp @@ -26,8 +26,63 @@ class read_test , public test::enable_yield_to { public: + struct fail_body + { + class reader; + + class value_type + { + friend class reader; + + std::string s_; + test::fail_counter& fc_; + + public: + explicit + value_type(test::fail_counter& fc) + : fc_(fc) + { + } + + value_type& + operator=(std::string s) + { + s_ = std::move(s); + return *this; + } + }; + + class reader + { + std::size_t n_ = 0; + value_type& body_; + + public: + template + explicit + reader(message& msg) noexcept + : body_(msg.body) + { + } + + void + init(error_code& ec) noexcept + { + body_.fc_.fail(ec); + } + + void + write(void const* data, + std::size_t size, error_code& ec) noexcept + { + if(body_.fc_.fail(ec)) + return; + } + }; + }; + template - void failMatrix(const char* s, yield_context do_yield) + void failMatrix(char const* s, yield_context do_yield) { using boost::asio::buffer; using boost::asio::buffer_copy; @@ -96,6 +151,20 @@ public: break; } BEAST_EXPECT(n < limit); + for(n = 0; n < limit; ++n) + { + streambuf sb; + sb.commit(buffer_copy( + sb.prepare(len), buffer(s, len))); + test::fail_counter fc{n}; + test::string_stream ss{ios_, s}; + parser_v1 p{fc}; + error_code ec; + parse(ss, sb, p, ec); + if(! ec) + break; + } + BEAST_EXPECT(n < limit); } void testThrow()