diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7bd7797a..8a67adca 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,7 @@ API Changes:
- Remove is_ReadableBody, is_WritableBody
- Add has_reader, has_writer, is_Reader, is_Writer
- More friendly compile errors on failed concept checks
+* basic_parser_v1 requires all callbacks present
--------------------------------------------------------------------------------
diff --git a/include/beast/http/basic_parser_v1.hpp b/include/beast/http/basic_parser_v1.hpp
index 78654a36..fbd54743 100644
--- a/include/beast/http/basic_parser_v1.hpp
+++ b/include/beast/http/basic_parser_v1.hpp
@@ -140,72 +140,76 @@ static std::uint64_t constexpr no_content_length =
more calls to derived class members functions (referred to as
"callbacks" from here on) matching a specific signature.
- Callbacks are detected through SFINAE. The derived class may
- implement as few or as many of the members as needed. Callbacks
- may not throw exceptions. These are the signatures of the
- callbacks:
+ Every callback must be provided by the derived class, or else
+ a compilation error will be generated. This exemplar shows
+ the signature and description of the callbacks required in
+ the derived class.
- @li `void on_start(error_code&)`
+ @code
+ template
+ struct exemplar : basic_parser_v1
+ {
+ // Called when the first valid octet of a new message is received
+ //
+ void on_start(error_code&);
- Called when the first valid octet of a new message is received
+ // Called for each piece of the Request-Method
+ //
+ void on_method(boost::string_ref const&, error_code&);
- @li `void on_method(boost::string_ref const&, error_code&)`
+ // Called for each piece of the Request-URI
+ //
+ void on_uri(boost::string_ref const&, error_code&);
- Called for each piece of the Request-Method
+ // Called for each piece of the reason-phrase
+ //
+ void on_reason(boost::string_ref const&, error_code&);
- @li `void on_uri(boost::string_ref const&, error_code&)`
+ // Called after the entire Request-Line has been parsed successfully.
+ //
+ void on_request(error_code&);
- Called for each piece of the Request-URI
+ // Called after the entire Response-Line has been parsed successfully.
+ //
+ void on_response(error_code&);
- @li `void on_reason(boost::string_ref const&, error_code&)`
+ // Called for each piece of the current header field.
+ //
+ void on_field(boost::string_ref const&, error_code&);
- Called for each piece of the reason-phrase
+ // Called for each piece of the current header value.
+ //
+ void on_value(boost::string_ref const&, error_code&)
- @li `void on_request(error_code&)`
+ // Called when all the headers have been parsed successfully.
+ //
+ body_what
+ on_headers(std::uint64_t content_length, error_code&);
- Called after the entire Request-Line has been parsed successfully.
+ // Called for each piece of the body.
+ //
+ // If the headers indicate chunk encoding, the chunk
+ // encoding is removed from the buffer before being
+ // passed to the callback.
+ //
+ void on_body(boost::string_ref const&, error_code&);
- @li `void on_response(error_code&)`
-
- Called after the entire Response-Line has been parsed successfully.
-
- @li `void on_field(boost::string_ref const&, error_code&)`
-
- Called for each piece of the current header field.
-
- @li `void on_value(boost::string_ref const&, error_code&)`
-
- Called for each piece of the current header value.
-
- @li `body_what on_headers(std::uint64_t content_length, error_code&)`
-
- Called when all the headers have been parsed successfully.
-
- @li `void on_body(boost::string_ref const&, error_code&)`
-
- Called for each piece of the body. If the headers indicated
- chunked encoding, the chunk encoding is removed from the
- buffer before being passed to the callback.
-
- @li `void on_complete(error_code&)`
-
- Called when the entire message has been parsed successfully.
- At this point, @ref complete returns `true`, and the parser
- is ready to parse another message if @ref keep_alive would
- return `true`.
+ // Called when the entire message has been parsed successfully.
+ // At this point, @ref complete returns `true`, and the parser
+ // is ready to parse another message if `keep_alive` would
+ // return `true`.
+ //
+ void on_complete(error_code&) {}
+ };
+ @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 parser uses traits to determine if the callback is possible.
- 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.
-
If a callback sets an error, parsing stops at the current octet
- and the error is returned to the caller.
+ and the error is returned to the caller. Callbacks must not throw
+ exceptions.
@tparam isRequest A `bool` indicating whether the parser will be
presented with request or response message.
@@ -514,161 +518,92 @@ private:
bool
needs_eof(std::false_type) const;
- template
- class check_on_start_t
- {
- template().on_start(
- std::declval()),
- std::true_type{})>
- static R check(int);
- template
- static std::false_type check(...);
- using type = decltype(check(0));
- public:
- static bool const value = type::value;
- };
- template
- using check_on_start =
- std::integral_constant::value>;
+ template>
+ struct check_on_start : std::false_type {};
- template
- class check_on_method_t
- {
- template().on_method(
- std::declval(),
- std::declval()),
- std::true_type{})>
- static R check(int);
- template
- static std::false_type check(...);
- using type = decltype(check(0));
- public:
- static bool const value = type::value;
- };
- template
- using check_on_method =
- std::integral_constant::value>;
+ template
+ struct check_on_start().on_start(
+ std::declval())
+ )>> : std::true_type { };
- template
- class check_on_uri_t
- {
- template().on_uri(
- std::declval(),
- std::declval()),
- std::true_type{})>
- static R check(int);
- template
- static std::false_type check(...);
- using type = decltype(check(0));
- public:
- static bool const value = type::value;
- };
- template
- using check_on_uri =
- std::integral_constant::value>;
+ template>
+ struct check_on_method : std::false_type {};
- template
- class check_on_reason_t
- {
- template().on_reason(
- std::declval(),
- std::declval()),
- std::true_type{})>
- static R check(int);
- template
- static std::false_type check(...);
- using type = decltype(check(0));
- public:
- static bool const value = type::value;
- };
- template
- using check_on_reason =
- std::integral_constant::value>;
+ template
+ struct check_on_method().on_method(
+ std::declval(),
+ std::declval())
+ )>> : std::true_type {};
- template
- class check_on_request_t
- {
- template().on_request(
- std::declval()),
- std::true_type{})>
- static R check(int);
- template
- static std::false_type check(...);
- using type = decltype(check(0));
- public:
- static bool const value = type::value;
- };
- template
- using check_on_request =
- std::integral_constant::value>;
+ template>
+ struct check_on_uri : std::false_type {};
- template
- class check_on_response_t
- {
- template().on_response(
- std::declval()),
- std::true_type{})>
- static R check(int);
- template
- static std::false_type check(...);
- using type = decltype(check(0));
- public:
- static bool const value = type::value;
- };
- template
- using check_on_response =
- std::integral_constant::value>;
+ template
+ struct check_on_uri().on_uri(
+ std::declval(),
+ std::declval())
+ )>> : std::true_type {};
- template
- class check_on_field_t
- {
- template().on_uri(
- std::declval(),
- std::declval()),
- std::true_type{})>
- static R check(int);
- template
- static std::false_type check(...);
- using type = decltype(check(0));
- public:
- static bool const value = type::value;
- };
- template
- using check_on_field =
- std::integral_constant::value>;
+ template>
+ struct check_on_reason : std::false_type {};
- template
- class check_on_value_t
- {
- template().on_uri(
- std::declval(),
- std::declval()),
- std::true_type{})>
- static R check(int);
- template
- static std::false_type check(...);
- using type = decltype(check(0));
- public:
- static bool const value = type::value;
- };
- template
- using check_on_value =
- std::integral_constant::value>;
+ template
+ struct check_on_reason().on_reason(
+ std::declval(),
+ std::declval())
+ )>> : std::true_type {};
+ template>
+ struct check_on_request : std::false_type {};
+
+ template
+ struct check_on_request().on_request(
+ std::declval())
+ )>> : std::true_type {};
+
+ template>
+ struct check_on_response : std::false_type {};
+
+ template
+ struct check_on_response().on_response(
+ std::declval())
+ )>> : std::true_type {};
+
+ template>
+ struct check_on_field : std::false_type {};
+
+ template
+ struct check_on_field().on_field(
+ std::declval(),
+ std::declval())
+ )>> : std::true_type {};
+
+ template>
+ struct check_on_value : std::false_type {};
+
+ template
+ struct check_on_value().on_value(
+ std::declval(),
+ std::declval())
+ )>> : std::true_type {};
+
+ // VFALCO Can we use std::is_detected? Is C++11 capable?
template
class check_on_headers_t
{
- template().on_headers(
- std::declval(), std::declval()))>>
+ template().on_headers(
+ std::declval(),
+ std::declval())),
+ body_what>>
static R check(int);
template
static std::false_type check(...);
@@ -680,67 +615,43 @@ private:
using check_on_headers =
std::integral_constant::value>;
- template
- class check_on_body_t
- {
- template().on_body(
- std::declval(),
- std::declval()),
- std::true_type{})>
- static R check(int);
- template
- static std::false_type check(...);
- using type = decltype(check(0));
- public:
- static bool const value = type::value;
- };
- template
- using check_on_body =
- std::integral_constant::value>;
+ template>
+ struct check_on_body : std::false_type {};
- template
- class check_on_complete_t
- {
- template().on_complete(
- std::declval()),
- std::true_type{})>
- static R check(int);
- template
- static std::false_type check(...);
- using type = decltype(check(0));
- public:
- static bool const value = type::value;
- };
- template
- using check_on_complete =
- std::integral_constant::value>;
+ template
+ struct check_on_body().on_body(
+ std::declval(),
+ std::declval())
+ )>> : std::true_type {};
-#undef BEAST_HAS_MEMBER
+ template>
+ struct check_on_complete : std::false_type {};
- void call_on_start(error_code& ec, std::true_type)
+ template
+ struct check_on_complete().on_complete(
+ std::declval())
+ )>> : std::true_type {};
+
+ void call_on_start(error_code& ec)
{
static_assert(check_on_start::value,
"on_start requirements not met");
impl().on_start(ec);
}
- void call_on_start(error_code& ec, std::false_type)
- {
- }
-
- void call_on_start(error_code& ec)
- {
- call_on_start(ec, std::is_member_function_pointer<
- decltype(&Derived::on_start)>{});
- }
-
void call_on_method(error_code& ec,
boost::string_ref const& s, std::true_type)
{
static_assert(check_on_method::value,
"on_method requirements not met");
+ if(h_max_ && s.size() > h_left_)
+ {
+ ec = parse_error::headers_too_big;
+ return;
+ }
+ h_left_ -= s.size();
impl().on_method(s, ec);
}
@@ -752,17 +663,8 @@ private:
void call_on_method(error_code& ec,
boost::string_ref const& s)
{
- if(! h_max_ || s.size() <= h_left_)
- {
- h_left_ -= s.size();
- call_on_method(ec, s, std::integral_constant::value>{});
- }
- else
- {
- ec = parse_error::headers_too_big;
- }
+ call_on_method(ec, s,
+ std::integral_constant{});
}
void call_on_uri(error_code& ec,
@@ -770,6 +672,12 @@ private:
{
static_assert(check_on_uri::value,
"on_uri requirements not met");
+ if(h_max_ && s.size() > h_left_)
+ {
+ ec = parse_error::headers_too_big;
+ return;
+ }
+ h_left_ -= s.size();
impl().on_uri(s, ec);
}
@@ -778,19 +686,11 @@ private:
{
}
- void call_on_uri(error_code& ec, boost::string_ref const& s)
+ void call_on_uri(error_code& ec,
+ boost::string_ref const& s)
{
- if(! h_max_ || s.size() <= h_left_)
- {
- h_left_ -= s.size();
- call_on_uri(ec, s, std::integral_constant::value>{});
- }
- else
- {
- ec = parse_error::headers_too_big;
- }
+ call_on_uri(ec, s,
+ std::integral_constant{});
}
void call_on_reason(error_code& ec,
@@ -798,6 +698,12 @@ private:
{
static_assert(check_on_reason::value,
"on_reason requirements not met");
+ if(h_max_ && s.size() > h_left_)
+ {
+ ec = parse_error::headers_too_big;
+ return;
+ }
+ h_left_ -= s.size();
impl().on_reason(s, ec);
}
@@ -808,17 +714,8 @@ private:
void call_on_reason(error_code& ec, boost::string_ref const& s)
{
- if(! h_max_ || s.size() <= h_left_)
- {
- h_left_ -= s.size();
- call_on_reason(ec, s, std::integral_constant::value>{});
- }
- else
- {
- ec = parse_error::headers_too_big;
- }
+ call_on_reason(ec, s,
+ std::integral_constant{});
}
void call_on_request(error_code& ec, std::true_type)
@@ -834,9 +731,8 @@ private:
void call_on_request(error_code& ec)
{
- call_on_request(ec, std::integral_constant::value>{});
+ call_on_request(ec,
+ std::integral_constant{});
}
void call_on_response(error_code& ec, std::true_type)
@@ -852,133 +748,65 @@ private:
void call_on_response(error_code& ec)
{
- call_on_response(ec, std::integral_constant::value>{});
+ call_on_response(ec,
+ std::integral_constant{});
}
void call_on_field(error_code& ec,
- boost::string_ref const& s, std::true_type)
+ boost::string_ref const& s)
{
static_assert(check_on_field::value,
"on_field requirements not met");
+ if(h_max_ && s.size() > h_left_)
+ {
+ ec = parse_error::headers_too_big;
+ return;
+ }
+ h_left_ -= s.size();
impl().on_field(s, ec);
}
- void call_on_field(error_code&,
- boost::string_ref const&, std::false_type)
- {
- }
-
- void call_on_field(error_code& ec, boost::string_ref const& s)
- {
- if(! h_max_ || s.size() <= h_left_)
- {
- h_left_ -= s.size();
- call_on_field(ec, s,
- std::is_member_function_pointer<
- decltype(&Derived::on_field)>{});
- }
- else
- {
- ec = parse_error::headers_too_big;
- }
- }
-
void call_on_value(error_code& ec,
- boost::string_ref const& s, std::true_type)
+ boost::string_ref const& s)
{
static_assert(check_on_value::value,
"on_value requirements not met");
- impl().on_value(s, ec);
- }
-
- void call_on_value(error_code&,
- boost::string_ref const&, std::false_type)
- {
- }
-
- void call_on_value(error_code& ec, boost::string_ref const& s)
- {
- if(! h_max_ || s.size() <= h_left_)
- {
- h_left_ -= s.size();
- call_on_value(ec, s,
- std::is_member_function_pointer<
- decltype(&Derived::on_value)>{});
- }
- else
+ if(h_max_ && s.size() > h_left_)
{
ec = parse_error::headers_too_big;
+ return;
}
- }
-
- body_what
- call_on_headers(error_code& ec,
- std::uint64_t content_length, std::true_type)
- {
- static_assert(check_on_headers::value,
- "on_headers requirements not met");
- return impl().on_headers(content_length, ec);
- }
-
- body_what
- call_on_headers(error_code& ec, std::uint64_t, std::false_type)
- {
- return body_what::normal;
+ h_left_ -= s.size();
+ impl().on_value(s, ec);
}
body_what
call_on_headers(error_code& ec)
{
- return call_on_headers(ec, content_length_,
- std::is_member_function_pointer<
- decltype(&Derived::on_headers)>{});
+ static_assert(check_on_headers::value,
+ "on_headers requirements not met");
+ return impl().on_headers(content_length_, ec);
}
void call_on_body(error_code& ec,
- boost::string_ref const& s, std::true_type)
+ boost::string_ref const& s)
{
static_assert(check_on_body::value,
"on_body requirements not met");
- impl().on_body(s, ec);
- }
-
- void call_on_body(error_code&,
- boost::string_ref const&, std::false_type)
- {
- }
-
- void call_on_body(error_code& ec, boost::string_ref const& s)
- {
- if(! b_max_ || s.size() <= b_left_)
- {
- b_left_ -= s.size();
- call_on_body(ec, s,
- std::is_member_function_pointer<
- decltype(&Derived::on_body)>{});
- }
- else
+ if(b_max_ && s.size() > b_left_)
{
ec = parse_error::body_too_big;
+ return;
}
- }
-
- void call_on_complete(error_code& ec, std::true_type)
- {
- static_assert(check_on_complete::value,
- "on_complete requirements not met");
- impl().on_complete(ec);
- }
-
- void call_on_complete(error_code&, std::false_type)
- {
+ b_left_ -= s.size();
+ impl().on_body(s, ec);
}
void call_on_complete(error_code& ec)
{
- call_on_complete(ec, std::is_member_function_pointer<
- decltype(&Derived::on_complete)>{});
+ static_assert(check_on_complete::value,
+ "on_complete requirements not met");
+ impl().on_complete(ec);
}
};