mirror of
https://github.com/boostorg/beast.git
synced 2025-07-31 21:34:46 +02:00
HTTP Reader (API Change):
fix #114, fix #117, fix #136 * 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
This commit is contained in:
@@ -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
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
@@ -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<decltype(m.body), Body::value_type>::value == true`
|
||||
* `m` denotes a value of type `message&` where
|
||||
`std::is_same<decltype(m.body), Body::value_type>::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]
|
||||
|
@@ -28,13 +28,13 @@ In this table:
|
||||
* `m` denotes a value of type `message const&` where
|
||||
`std::is_same<decltype(m.body), Body::value_type>: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<bool isRequest, class Body, class Headers>
|
||||
explicit
|
||||
writer(message<isRequest, Body, Headers> const& msg);
|
||||
writer(message<isRequest, Body, Headers> 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<class WriteFunction>
|
||||
boost::tribool
|
||||
operator()(resume_context&&, error_code&, WriteFunction&& write);
|
||||
operator()(
|
||||
resume_context&&,
|
||||
error_code&,
|
||||
WriteFunction&& write) noexcept;
|
||||
};
|
||||
```
|
||||
|
||||
|
@@ -41,6 +41,11 @@ private:
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code&) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
write(void const* data,
|
||||
std::size_t size, error_code&) noexcept
|
||||
|
@@ -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
|
||||
|
@@ -13,6 +13,7 @@
|
||||
#include <beast/http/message_v1.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
@@ -87,17 +88,28 @@ public:
|
||||
message_v1<isRequest, Body, Headers>;
|
||||
|
||||
private:
|
||||
using reader =
|
||||
typename message_type::body_type::reader;
|
||||
|
||||
static_assert(is_ReadableBody<Body>::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<reader> 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>(arg1),
|
||||
std::forward<ArgN>(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&)
|
||||
|
@@ -42,6 +42,11 @@ private:
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code&) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
write(void const* data,
|
||||
std::size_t size, error_code&) noexcept
|
||||
|
@@ -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<bool isRequest, class Allocator>
|
||||
explicit
|
||||
reader(message<isRequest, fail_body, Allocator>& 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<bool isRequest>
|
||||
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<isRequest, fail_body, headers> p{fc};
|
||||
error_code ec;
|
||||
parse(ss, sb, p, ec);
|
||||
if(! ec)
|
||||
break;
|
||||
}
|
||||
BEAST_EXPECT(n < limit);
|
||||
}
|
||||
|
||||
void testThrow()
|
||||
|
Reference in New Issue
Block a user