mirror of
https://github.com/boostorg/beast.git
synced 2025-08-02 14:24:31 +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
|
* Fix basic_streambuf::capacity
|
||||||
* Add basic_streambuf::alloc_size
|
* Add basic_streambuf::alloc_size
|
||||||
* Parser callbacks may not throw
|
* 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]
|
[section:Reader Reader requirements]
|
||||||
|
|
||||||
Parsers provided by the implementation will construct the corresponding
|
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.
|
Body to determine the strategy for storing incoming message body data.
|
||||||
|
|
||||||
In this table:
|
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
|
* `m` denotes a value of type `message&` where
|
||||||
`std::is_same<decltype(m.body), Body::value_type>::value == true`
|
`std::is_same<decltype(m.body), Body::value_type>::value == true`.
|
||||||
|
|
||||||
[table Reader requirements
|
[table Reader requirements
|
||||||
[[operation] [type] [semantics, pre/post-conditions]]
|
[[operation] [type] [semantics, pre/post-conditions]]
|
||||||
@@ -32,22 +32,38 @@ In this table:
|
|||||||
[`X a(m);`]
|
[`X a(m);`]
|
||||||
[]
|
[]
|
||||||
[
|
[
|
||||||
`a` is constructible from `m`. The lifetime of `m` is
|
`a` is constructible from `m`. The lifetime of `m` is guaranteed
|
||||||
guaranteed to end no earlier than after `a` is destroyed.
|
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)`]
|
[`a.write(p, n, ec)`]
|
||||||
[`void`]
|
[`void`]
|
||||||
[
|
[
|
||||||
Deserializes the input sequence into the body.
|
Deserializes the input sequence into the body. If `ec` is set,
|
||||||
If `ec` is set, the deserialization is aborted and the error
|
the deserialization is aborted and the error is propagated to
|
||||||
is returned to the caller.
|
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
|
[note
|
||||||
inline so the generated code becomes part of the implementation. ]
|
Definitions for required `Reader` member functions should be declared
|
||||||
|
inline so the generated code can become part of the implementation.
|
||||||
|
]
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
@@ -28,13 +28,13 @@ In this table:
|
|||||||
* `m` denotes a value of type `message const&` where
|
* `m` denotes a value of type `message const&` where
|
||||||
`std::is_same<decltype(m.body), Body::value_type>:value == true`.
|
`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
|
* `wf` is a [*write function]: a function object of unspecified type provided
|
||||||
by the implementation which accepts any value meeting the requirements
|
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
|
[table Writer requirements
|
||||||
[[operation] [type] [semantics, pre/post-conditions]]
|
[[operation] [type] [semantics, pre/post-conditions]]
|
||||||
@@ -42,17 +42,18 @@ In this table:
|
|||||||
[`X a(m);`]
|
[`X a(m);`]
|
||||||
[]
|
[]
|
||||||
[
|
[
|
||||||
`a` is constructible from `m`. The lifetime of `m` is
|
`a` is constructible from `m`. The lifetime of `m` is guaranteed
|
||||||
guaranteed to end no earlier than after `a` is destroyed.
|
to end no earlier than after `a` is destroyed. This function must
|
||||||
|
be `noexcept`.
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
[
|
[
|
||||||
[`a.init(ec)`]
|
[`a.init(ec)`]
|
||||||
[`void`]
|
[`void`]
|
||||||
[
|
[
|
||||||
Called immediately after construction.
|
Called immediately after construction. If the function sets an
|
||||||
If `ec` is set, the serialization is aborted and the error
|
error code in `ec`, the serialization is aborted and the error
|
||||||
is propagated to the caller.
|
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
|
the serialized message body will be sent unmodified, with the
|
||||||
error `boost::asio::error::eof` returned to the caller, to notify
|
error `boost::asio::error::eof` returned to the caller, to notify
|
||||||
they should close the connection to indicate the end of the message.
|
they should close the connection to indicate the end of the message.
|
||||||
|
This function must be `noexcept`.
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
[
|
[
|
||||||
[`a(rc, ec, wf)`]
|
[`a(rc, ec, wf)`]
|
||||||
[`boost::tribool`]
|
[`boost::tribool`]
|
||||||
[
|
[
|
||||||
Called repeatedly after `init` succeeds.
|
Called repeatedly after `init` succeeds. `wf` is a function object
|
||||||
`wf` is a function object which takes as its single parameter,
|
which takes as its single parameter any value meeting the requirements
|
||||||
any value meeting the requirements of `ConstBufferSequence`.
|
of __ConstBufferSequence__. Buffers provided to this write function
|
||||||
Buffers provided by the `writer` to this [*write function] must
|
must remain valid until the next member function of `writer` is
|
||||||
remain valid until the next member function of `writer` is
|
|
||||||
invoked (which may be the destructor). This function returns `true`
|
invoked (which may be the destructor). This function returns `true`
|
||||||
to indicate all message body data has been written, or `false`
|
to indicate all message body data has been written, or `false` if
|
||||||
if there is more body data. If the return value is
|
there is more body data. If the return value is `boost::indeterminate`,
|
||||||
`boost::indeterminate`, the implementation will suspend the operation
|
the implementation will suspend the operation until the writer invokes
|
||||||
until the writer invokes `rc`. It is the writers responsibility when
|
`rc`. It is the writers responsibility when returning
|
||||||
returning `boost::indeterminate`, to acquire ownership of the
|
`boost::indeterminate`, to acquire ownership of `rc` via move
|
||||||
`resume_context` via move construction and eventually call it or else
|
construction and eventually call it or else undefined behavior
|
||||||
undefined behavior results.
|
results. This function must be `noexcept`.
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
[note Definitions for required `Writer` member functions should be declared
|
[note
|
||||||
inline so the generated code becomes part of the implementation. ]
|
Definitions for required `Writer` member functions should be declared
|
||||||
|
inline so the generated code can become part of the implementation.
|
||||||
|
]
|
||||||
|
|
||||||
Exemplar:
|
Exemplar:
|
||||||
```
|
```
|
||||||
@@ -109,7 +112,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
template<bool isRequest, class Body, class Headers>
|
template<bool isRequest, class Body, class Headers>
|
||||||
explicit
|
explicit
|
||||||
writer(message<isRequest, Body, Headers> const& msg);
|
writer(message<isRequest, Body, Headers> const& msg) noexcept;
|
||||||
|
|
||||||
/** Initialize the writer.
|
/** Initialize the writer.
|
||||||
|
|
||||||
@@ -119,7 +122,7 @@ public:
|
|||||||
@param ec Contains the error code if any errors occur.
|
@param ec Contains the error code if any errors occur.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
init(error_code& ec);
|
init(error_code& ec) noexcept;
|
||||||
|
|
||||||
/** Returns the content length.
|
/** Returns the content length.
|
||||||
|
|
||||||
@@ -129,7 +132,7 @@ public:
|
|||||||
of the message.
|
of the message.
|
||||||
*/
|
*/
|
||||||
std::uint64_t
|
std::uint64_t
|
||||||
content_length();
|
content_length() noexcept;
|
||||||
|
|
||||||
/** Write zero or one buffer representing the message body.
|
/** Write zero or one buffer representing the message body.
|
||||||
|
|
||||||
@@ -172,7 +175,10 @@ public:
|
|||||||
*/
|
*/
|
||||||
template<class WriteFunction>
|
template<class WriteFunction>
|
||||||
boost::tribool
|
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
|
void
|
||||||
write(void const* data,
|
write(void const* data,
|
||||||
std::size_t size, error_code&) noexcept
|
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&)`
|
@li `void on_complete(error_code&)`
|
||||||
|
|
||||||
Called when the entire message has been parsed successfully.
|
Called when the entire message has been parsed successfully.
|
||||||
At this point, @ref basic_parser_v1::complete returns `true`, and
|
At this point, @ref complete returns `true`, and the parser
|
||||||
the parser is ready to parse another message if keep_alive
|
is ready to parse another message if @ref keep_alive would
|
||||||
would return `true`.
|
return `true`.
|
||||||
|
|
||||||
The return value of `on_headers` is special, it controls whether
|
The return value of `on_headers` is special, it controls whether
|
||||||
or not the parser should expect a body. See @ref body_what for
|
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/http/message_v1.hpp>
|
||||||
#include <beast/core/error.hpp>
|
#include <beast/core/error.hpp>
|
||||||
#include <boost/assert.hpp>
|
#include <boost/assert.hpp>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
@@ -87,17 +88,28 @@ public:
|
|||||||
message_v1<isRequest, Body, Headers>;
|
message_v1<isRequest, Body, Headers>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
using reader =
|
||||||
|
typename message_type::body_type::reader;
|
||||||
|
|
||||||
static_assert(is_ReadableBody<Body>::value,
|
static_assert(is_ReadableBody<Body>::value,
|
||||||
"ReadableBody requirements not met");
|
"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 field_;
|
||||||
std::string value_;
|
std::string value_;
|
||||||
message_type m_;
|
message_type m_;
|
||||||
typename message_type::body_type::reader r_;
|
boost::optional<reader> r_;
|
||||||
std::uint8_t skip_body_ = 0;
|
std::uint8_t skip_body_ = 0;
|
||||||
bool flush_ = false;
|
bool flush_ = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
/// Default constructor
|
||||||
|
parser_v1() = default;
|
||||||
|
|
||||||
/// Move constructor
|
/// Move constructor
|
||||||
parser_v1(parser_v1&&) = default;
|
parser_v1(parser_v1&&) = default;
|
||||||
|
|
||||||
@@ -110,12 +122,6 @@ public:
|
|||||||
/// Copy assignment (disallowed)
|
/// Copy assignment (disallowed)
|
||||||
parser_v1& operator=(parser_v1 const&) = delete;
|
parser_v1& operator=(parser_v1 const&) = delete;
|
||||||
|
|
||||||
/// Default constructor
|
|
||||||
parser_v1()
|
|
||||||
: r_(m_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Construct the parser.
|
/** Construct the parser.
|
||||||
|
|
||||||
@param args A list of arguments forwarded to the message constructor.
|
@param args A list of arguments forwarded to the message constructor.
|
||||||
@@ -127,7 +133,6 @@ public:
|
|||||||
parser_v1(Arg1&& arg1, ArgN&&... argn)
|
parser_v1(Arg1&& arg1, ArgN&&... argn)
|
||||||
: m_(std::forward<Arg1>(arg1),
|
: m_(std::forward<Arg1>(arg1),
|
||||||
std::forward<ArgN>(argn)...)
|
std::forward<ArgN>(argn)...)
|
||||||
, r_(m_)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,12 +237,14 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
body_what
|
body_what
|
||||||
on_headers(std::uint64_t, error_code&)
|
on_headers(std::uint64_t, error_code& ec)
|
||||||
{
|
{
|
||||||
flush();
|
flush();
|
||||||
m_.version = 10 * this->http_major() + this->http_minor();
|
m_.version = 10 * this->http_major() + this->http_minor();
|
||||||
if(skip_body_)
|
if(skip_body_)
|
||||||
return body_what::skip;
|
return body_what::skip;
|
||||||
|
r_.emplace(m_);
|
||||||
|
r_->init(ec);
|
||||||
return body_what::normal;
|
return body_what::normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,7 +262,7 @@ private:
|
|||||||
|
|
||||||
void on_body(boost::string_ref const& s, error_code& ec)
|
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&)
|
void on_complete(error_code&)
|
||||||
|
@@ -42,6 +42,11 @@ private:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
init(error_code&) noexcept
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
write(void const* data,
|
write(void const* data,
|
||||||
std::size_t size, error_code&) noexcept
|
std::size_t size, error_code&) noexcept
|
||||||
|
@@ -26,8 +26,63 @@ class read_test
|
|||||||
, public test::enable_yield_to
|
, public test::enable_yield_to
|
||||||
{
|
{
|
||||||
public:
|
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>
|
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;
|
||||||
using boost::asio::buffer_copy;
|
using boost::asio::buffer_copy;
|
||||||
@@ -96,6 +151,20 @@ public:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
BEAST_EXPECT(n < limit);
|
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()
|
void testThrow()
|
||||||
|
Reference in New Issue
Block a user