forked from boostorg/beast
Add pause option to on_headers interface:
* on_headers returns enum body_what * body_what::pause lets caller receive headers during the parse
This commit is contained in:
@@ -80,6 +80,49 @@ struct body_max_size
|
||||
}
|
||||
};
|
||||
|
||||
/** A value indicating how the parser should treat the body.
|
||||
|
||||
This value is returned from the `on_headers` callback in
|
||||
the derived class. It controls what the parser does next
|
||||
in terms of the message body.
|
||||
*/
|
||||
enum class body_what
|
||||
{
|
||||
/** The parser should expect a body, keep reading.
|
||||
*/
|
||||
normal,
|
||||
|
||||
/** Skip parsing of the body.
|
||||
|
||||
When returned by `on_headers` this causes parsing to
|
||||
complete and control to return to the caller. This
|
||||
could be used when sending a response to a HEAD
|
||||
request, for example.
|
||||
*/
|
||||
skip,
|
||||
|
||||
/** The message represents an UPGRADE request.
|
||||
|
||||
When returned by `on_headers` 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.
|
||||
|
||||
This could be used by callers to inspect the HTTP
|
||||
headers before committing to read the body. For example,
|
||||
to choose the body type based on the headers. Or to
|
||||
respond to an Expect: 100-continue request.
|
||||
*/
|
||||
pause
|
||||
};
|
||||
|
||||
/// The value returned when no content length is known or applicable.
|
||||
static std::uint64_t constexpr no_content_length =
|
||||
std::numeric_limits<std::uint64_t>::max();
|
||||
@@ -133,7 +176,7 @@ static std::uint64_t constexpr no_content_length =
|
||||
|
||||
Called for each piece of the current header value.
|
||||
|
||||
@li `int on_headers(std::uint64_t content_length, error_code&)`
|
||||
@li `body_what on_headers(std::uint64_t content_length, error_code&)`
|
||||
|
||||
Called when all the headers have been parsed successfully.
|
||||
|
||||
@@ -151,18 +194,11 @@ static std::uint64_t constexpr no_content_length =
|
||||
would return `true`.
|
||||
|
||||
The return value of `on_headers` is special, it controls whether
|
||||
or not the parser should expect a body. These are the return values:
|
||||
|
||||
@li *0* The parser should expect a body
|
||||
|
||||
@li *1* The parser should skip the body. For example, this is
|
||||
used when sending a response to a HEAD request.
|
||||
|
||||
@li *2* The parser should skip ths body, this is an
|
||||
upgrade to a different protocol.
|
||||
or not the parser should expect a body. See @ref body_what for
|
||||
choices of the return value.
|
||||
|
||||
The parser uses traits to determine if the callback is possible.
|
||||
If the Derived type omits one or more callbacks, they are simply
|
||||
If the @b Derived type omits one or more callbacks, they are simply
|
||||
skipped with no compilation error. The default behavior of `on_body`
|
||||
when the derived class does not provide the member, is to specify that
|
||||
the body should not be skipped.
|
||||
@@ -172,6 +208,9 @@ static std::uint64_t constexpr no_content_length =
|
||||
|
||||
@tparam isRequest A `bool` indicating whether the parser will be
|
||||
presented with request or response message.
|
||||
|
||||
@tparam Derived The derived class type. This is part of the
|
||||
Curiously Recurring Template Pattern interface.
|
||||
*/
|
||||
template<bool isRequest, class Derived>
|
||||
class basic_parser_v1 : public detail::parser_base
|
||||
@@ -626,7 +665,7 @@ private:
|
||||
template<class C>
|
||||
class has_on_headers_t
|
||||
{
|
||||
template<class T, class R = std::is_same<int,
|
||||
template<class T, class R = std::is_convertible<body_what,
|
||||
decltype(std::declval<T>().on_headers(
|
||||
std::declval<std::uint64_t>(), std::declval<error_code&>()))>>
|
||||
static R check(int);
|
||||
@@ -845,18 +884,21 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
int call_on_headers(error_code& ec,
|
||||
body_what
|
||||
call_on_headers(error_code& ec,
|
||||
std::uint64_t content_length, std::true_type)
|
||||
{
|
||||
return impl().on_headers(content_length, ec);
|
||||
}
|
||||
|
||||
int call_on_headers(error_code& ec, std::uint64_t, std::false_type)
|
||||
body_what
|
||||
call_on_headers(error_code& ec, std::uint64_t, std::false_type)
|
||||
{
|
||||
return 0;
|
||||
return body_what::normal;
|
||||
}
|
||||
|
||||
int call_on_headers(error_code& ec)
|
||||
body_what
|
||||
call_on_headers(error_code& ec)
|
||||
{
|
||||
return call_on_headers(ec, content_length_,
|
||||
has_on_headers<Derived>{});
|
||||
|
||||
@@ -899,21 +899,22 @@ 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 maybe_skip = call_on_headers(ec);
|
||||
auto const what = call_on_headers(ec);
|
||||
if(ec)
|
||||
return errc();
|
||||
switch(maybe_skip)
|
||||
switch(what)
|
||||
{
|
||||
case 0:
|
||||
case body_what::normal:
|
||||
break;
|
||||
case 2:
|
||||
case body_what::upgrade:
|
||||
upgrade_ = true;
|
||||
// fall through
|
||||
case 1:
|
||||
case body_what::skip:
|
||||
flags_ |= parse_flag::skipbody;
|
||||
break;
|
||||
default:
|
||||
return err(parse_error::bad_on_headers_rv);
|
||||
case body_what::pause:
|
||||
s_ = s_headers_done;
|
||||
return used();
|
||||
}
|
||||
s_ = s_headers_done;
|
||||
// fall through
|
||||
|
||||
@@ -47,7 +47,6 @@ public:
|
||||
case parse_error::bad_value: return "bad field-value";
|
||||
case parse_error::bad_content_length: return "bad Content-Length";
|
||||
case parse_error::illegal_content_length: return "illegal Content-Length with chunked Transfer-Encoding";
|
||||
case parse_error::bad_on_headers_rv: return "on_headers returned an unknown value";
|
||||
case parse_error::invalid_chunk_size: return "invalid chunk size";
|
||||
case parse_error::invalid_ext_name: return "invalid ext name";
|
||||
case parse_error::invalid_ext_val: return "invalid ext val";
|
||||
|
||||
@@ -29,7 +29,6 @@ enum class parse_error
|
||||
bad_value,
|
||||
bad_content_length,
|
||||
illegal_content_length,
|
||||
bad_on_headers_rv,
|
||||
|
||||
invalid_chunk_size,
|
||||
invalid_ext_name,
|
||||
|
||||
@@ -231,11 +231,14 @@ private:
|
||||
m_.reason = std::move(this->reason_);
|
||||
}
|
||||
|
||||
int on_headers(std::uint64_t, error_code&)
|
||||
body_what
|
||||
on_headers(std::uint64_t, error_code&)
|
||||
{
|
||||
flush();
|
||||
m_.version = 10 * this->http_major() + this->http_minor();
|
||||
return skip_body_;
|
||||
if(skip_body_)
|
||||
return body_what::skip;
|
||||
return body_what::normal;
|
||||
}
|
||||
|
||||
void on_request(error_code& ec)
|
||||
|
||||
Reference in New Issue
Block a user