Add on_body_what parser callback (API Change):

These changes support parsing the headers separately from the body.

* on_headers now returns void
* on_body_what is a new required callback which returns body_what
This commit is contained in:
Vinnie Falco
2016-10-16 17:40:21 -04:00
parent ac07d0c3ed
commit 83556937f6
7 changed files with 73 additions and 20 deletions

View File

@ -23,6 +23,8 @@ API Changes:
- Add has_reader, has_writer, is_Reader, is_Writer
- More friendly compile errors on failed concept checks
* basic_parser_v1 requires all callbacks present
* on_headers parser callback now returns void
* on_body_what is a new required parser callback returning body_what
--------------------------------------------------------------------------------

View File

@ -103,17 +103,17 @@ enum class body_what
/** The message represents an UPGRADE request.
When returned by `on_headers` this causes parsing
When returned by `on_body_prepare` this causes parsing
to complete and control to return to the caller.
*/
upgrade,
/** Suspend parsing before reading the body.
When returned by `on_headers` this causes parsing to
pause. Control is returned to the caller, and the parser
state is preserved such that a subsequent call to the
parser will begin reading the message body.
When returned by `on_body_prepare` this causes parsing
to pause. Control is returned to the caller, and the
parser state is preserved such that a subsequent call
to the parser will begin reading the message body.
This could be used by callers to inspect the HTTP
headers before committing to read the body. For example,
@ -183,9 +183,14 @@ static std::uint64_t constexpr no_content_length =
// Called when all the headers have been parsed successfully.
//
body_what
void
on_headers(std::uint64_t content_length, error_code&);
// Called after on_headers, before the body is parsed
//
body_what
on_body_what(std::uint64_t content_length, error_code&);
// Called for each piece of the body.
//
// If the headers indicate chunk encoding, the chunk
@ -203,9 +208,9 @@ static std::uint64_t constexpr no_content_length =
};
@endcode
The return value of `on_headers` is special, it controls whether
or not the parser should expect a body. See @ref body_what for
choices of the return value.
The return value of `on_body_what` is special, it controls
whether or not the parser should expect a body. See @ref body_what
for choices of the return value.
If a callback sets an error, parsing stops at the current octet
and the error is returned to the caller. Callbacks must not throw
@ -595,12 +600,22 @@ private:
std::declval<error_code&>())
)>> : std::true_type {};
template<class T, class = beast::detail::void_t<>>
struct check_on_headers : std::false_type {};
template<class T>
struct check_on_headers<T, beast::detail::void_t<decltype(
std::declval<T>().on_headers(
std::declval<std::uint64_t>(),
std::declval<error_code&>())
)>> : std::true_type {};
// VFALCO Can we use std::is_detected? Is C++11 capable?
template<class C>
class check_on_headers_t
class check_on_body_what_t
{
template<class T, class R = std::is_convertible<decltype(
std::declval<T>().on_headers(
std::declval<T>().on_body_what(
std::declval<std::uint64_t>(),
std::declval<error_code&>())),
body_what>>
@ -612,8 +627,8 @@ private:
static bool const value = type::value;
};
template<class C>
using check_on_headers =
std::integral_constant<bool, check_on_headers_t<C>::value>;
using check_on_body_what =
std::integral_constant<bool, check_on_body_what_t<C>::value>;
template<class T, class = beast::detail::void_t<>>
struct check_on_body : std::false_type {};
@ -780,12 +795,20 @@ private:
impl().on_value(s, ec);
}
body_what
void
call_on_headers(error_code& ec)
{
static_assert(check_on_headers<Derived>::value,
"on_headers requirements not met");
return impl().on_headers(content_length_, ec);
impl().on_headers(content_length_, ec);
}
body_what
call_on_body_what(error_code& ec)
{
static_assert(check_on_body_what<Derived>::value,
"on_body_what requirements not met");
return impl().on_body_what(content_length_, ec);
}
void call_on_body(error_code& ec,

View File

@ -127,6 +127,7 @@ protected:
s_chunk_data_cr,
s_chunk_data_lf,
s_body_what,
s_body_identity0,
s_body_identity,
s_body_identity_eof0,

View File

@ -899,7 +899,16 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
return err(parse_error::illegal_content_length);
upgrade_ = ((flags_ & (parse_flag::upgrade | parse_flag::connection_upgrade)) ==
(parse_flag::upgrade | parse_flag::connection_upgrade)) /*|| method == "connect"*/;
auto const what = call_on_headers(ec);
call_on_headers(ec);
if(ec)
return errc();
s_ = s_body_what;
// fall through
}
case s_body_what:
{
auto const what = call_on_body_what(ec);
if(ec)
return errc();
switch(what)
@ -913,7 +922,6 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
flags_ |= parse_flag::skipbody;
break;
case body_what::pause:
s_ = s_headers_done;
return used();
}
s_ = s_headers_done;

View File

@ -241,11 +241,16 @@ private:
m_.reason = std::move(this->reason_);
}
body_what
void
on_headers(std::uint64_t, error_code& ec)
{
flush();
m_.version = 10 * this->http_major() + this->http_minor();
}
body_what
on_body_what(std::uint64_t, error_code& ec)
{
if(skip_body_)
return body_what::skip;
r_.emplace(m_);

View File

@ -52,6 +52,7 @@ public:
bool field = false;
bool value = false;
bool headers = false;
bool _body_what = false;
bool body = false;
bool complete = false;
@ -90,10 +91,15 @@ public:
{
value = true;
}
body_what
void
on_headers(std::uint64_t, error_code&)
{
headers = true;
}
body_what
on_body_what(std::uint64_t, error_code&)
{
_body_what = true;
return body_what::normal;
}
void on_body(boost::string_ref const&, error_code&)
@ -130,6 +136,7 @@ public:
BEAST_EXPECT(p.field);
BEAST_EXPECT(p.value);
BEAST_EXPECT(p.headers);
BEAST_EXPECT(p._body_what);
BEAST_EXPECT(p.body);
BEAST_EXPECT(p.complete);
}

View File

@ -85,8 +85,15 @@ public:
fc_.fail(ec);
}
body_what
void
on_headers(std::uint64_t content_length, error_code& ec)
{
if(fc_.fail(ec))
return;
}
body_what
on_body_what(std::uint64_t content_length, error_code& ec)
{
if(fc_.fail(ec))
return body_what::normal;