diff --git a/CHANGELOG.md b/CHANGELOG.md
index eb336e4a..56a73e29 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
* Fix on_headers called twice from basic_parser_v1
* Constrain parser_v1 constructor
* Improve first line serialization
+* Add pause option to on_headers interface
* Refine Parser concept
API Changes:
diff --git a/doc/quickref.xml b/doc/quickref.xml
index 321a34f3..b9c5c647 100644
--- a/doc/quickref.xml
+++ b/doc/quickref.xml
@@ -68,6 +68,7 @@
Constants
+ body_whatconnectionConcepts
diff --git a/include/beast/http/basic_parser_v1.hpp b/include/beast/http/basic_parser_v1.hpp
index 86b9c2d5..b9425c49 100644
--- a/include/beast/http/basic_parser_v1.hpp
+++ b/include/beast/http/basic_parser_v1.hpp
@@ -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::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
class basic_parser_v1 : public detail::parser_base
@@ -626,7 +665,7 @@ private:
template
class has_on_headers_t
{
- template().on_headers(
std::declval(), std::declval()))>>
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{});
diff --git a/include/beast/http/impl/basic_parser_v1.ipp b/include/beast/http/impl/basic_parser_v1.ipp
index c64799a6..982a988e 100644
--- a/include/beast/http/impl/basic_parser_v1.ipp
+++ b/include/beast/http/impl/basic_parser_v1.ipp
@@ -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
diff --git a/include/beast/http/impl/parse_error.ipp b/include/beast/http/impl/parse_error.ipp
index 7f2d9dc2..155eb6eb 100644
--- a/include/beast/http/impl/parse_error.ipp
+++ b/include/beast/http/impl/parse_error.ipp
@@ -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";
diff --git a/include/beast/http/parse_error.hpp b/include/beast/http/parse_error.hpp
index 7463fbdb..72025f7d 100644
--- a/include/beast/http/parse_error.hpp
+++ b/include/beast/http/parse_error.hpp
@@ -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,
diff --git a/include/beast/http/parser_v1.hpp b/include/beast/http/parser_v1.hpp
index 5e1a7629..731d4595 100644
--- a/include/beast/http/parser_v1.hpp
+++ b/include/beast/http/parser_v1.hpp
@@ -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)
diff --git a/test/http/basic_parser_v1.cpp b/test/http/basic_parser_v1.cpp
index be2f13da..dc4270d4 100644
--- a/test/http/basic_parser_v1.cpp
+++ b/test/http/basic_parser_v1.cpp
@@ -90,10 +90,11 @@ public:
{
value = true;
}
- int on_headers(std::uint64_t, error_code&)
+ body_what
+ on_headers(std::uint64_t, error_code&)
{
headers = true;
- return 0;
+ return body_what::normal;
}
void on_body(boost::string_ref const&, error_code&)
{
@@ -194,7 +195,7 @@ public:
template
void
- good(int onBodyRv, std::string const& s, F const& f)
+ good(body_what onBodyRv, std::string const& s, F const& f)
{
using boost::asio::buffer;
for_split(s,
@@ -237,12 +238,12 @@ public:
void
good(std::string const& s, F const& f = {})
{
- return good(0, s, f);
+ return good(body_what::normal, s, f);
}
template
void
- bad(int onBodyRv, std::string const& s, error_code ev)
+ bad(body_what onBodyRv, std::string const& s, error_code ev)
{
using boost::asio::buffer;
for_split(s,
@@ -294,7 +295,7 @@ public:
void
bad(std::string const& s, error_code ev = {})
{
- return bad(0, s, ev);
+ return bad(body_what::normal, s, ev);
}
//--------------------------------------------------------------------------
@@ -658,7 +659,7 @@ public:
auto const length =
[&](std::string const& s, std::uint64_t v)
{
- good(1, s,
+ good(body_what::skip, s,
[&](fail_parser const& p)
{
BEAST_EXPECT(p.content_length() == v);
@@ -757,7 +758,7 @@ public:
good(m("Transfer-Encodings: 2\r\n"), flags{*this, 0});
good(m("Transfer-Encoded: false\r\n"), flags{*this, 0});
- bad(1,
+ bad(body_what::skip,
"HTTP/1.1 200 OK\r\n"
"Content-Length: 1\r\n"
"Transfer-Encoding: chunked\r\n"
@@ -848,8 +849,8 @@ public:
{
error_code ec;
test::fail_counter fc(1000);
- fail_parser p(fc);
- p.on_body_rv(2);
+ fail_parser p{fc};
+ p.on_body_rv(body_what::upgrade);
p.write(buf(
"GET / HTTP/1.1\r\n"
"Content-Length: 1\r\n"
@@ -1007,12 +1008,7 @@ public:
BEAST_EXPECT(p.complete());
}
- bad(3,
- "HTTP/1.1 200 OK\r\n"
- "Content-Length: 1\r\n"
- "\r\n*", parse_error::bad_on_headers_rv);
-
- bad(0,
+ bad(body_what::normal,
"GET / HTTP/1.1\r\n"
"Content-Length: 1\r\n"
"\r\n",
diff --git a/test/http/fail_parser.hpp b/test/http/fail_parser.hpp
index 003992a2..e55ca1e3 100644
--- a/test/http/fail_parser.hpp
+++ b/test/http/fail_parser.hpp
@@ -20,7 +20,7 @@ class fail_parser
{
test::fail_counter& fc_;
std::uint64_t content_length_ = no_content_length;
- int body_rv_ = 0;
+ body_what body_rv_ = body_what::normal;
public:
std::string body;
@@ -33,7 +33,7 @@ public:
}
void
- on_body_rv(int rv)
+ on_body_rv(body_what rv)
{
body_rv_ = rv;
}
@@ -85,10 +85,11 @@ public:
fc_.fail(ec);
}
- int on_headers(std::uint64_t content_length, error_code& ec)
+ body_what
+ on_headers(std::uint64_t content_length, error_code& ec)
{
if(fc_.fail(ec))
- return 0;
+ return body_what::normal;
content_length_ = content_length;
return body_rv_;
}
diff --git a/test/http/parse_error.cpp b/test/http/parse_error.cpp
index a20e6f4f..1bbb3025 100644
--- a/test/http/parse_error.cpp
+++ b/test/http/parse_error.cpp
@@ -45,7 +45,6 @@ public:
check("http", parse_error::bad_value);
check("http", parse_error::bad_content_length);
check("http", parse_error::illegal_content_length);
- check("http", parse_error::bad_on_headers_rv);
check("http", parse_error::invalid_chunk_size);
check("http", parse_error::short_read);
check("http", parse_error::general);