forked from boostorg/beast
rfc7230 compliance, limits, and tests for basic_parser_v1:
New parser set_option function for controlling independent size limits on headers and body. By default request and response parsers are set up with reasonable limits to prevent resource exhaustion attacks. * Parser adheres strictly to rfc7230 * Increased test coverage * Headers and body maximum size limit options
This commit is contained in:
@@ -6,6 +6,8 @@
|
|||||||
* Remove obsolete RFC2616 functions
|
* Remove obsolete RFC2616 functions
|
||||||
* Add message swap members and free functions
|
* Add message swap members and free functions
|
||||||
* Add HTTP field value parser containers: ext_list, param_list, token_list
|
* Add HTTP field value parser containers: ext_list, param_list, token_list
|
||||||
|
* Fixes for some corner cases in basic_parser_v1
|
||||||
|
* Configurable limits on headers and body sizes in basic_parser_v1
|
||||||
|
|
||||||
API Changes:
|
API Changes:
|
||||||
|
|
||||||
@@ -14,4 +16,6 @@ API Changes:
|
|||||||
* "DynamicBuffer","dynabuf" renamed from "Streambuf", "streambuf". See:
|
* "DynamicBuffer","dynabuf" renamed from "Streambuf", "streambuf". See:
|
||||||
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4478.html#requirements.dynamic_buffers
|
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4478.html#requirements.dynamic_buffers
|
||||||
|
|
||||||
|
* basic_parser_v1 adheres to rfc7230 as strictly as possible
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
8
TODO.txt
8
TODO.txt
@@ -46,3 +46,11 @@ HTTP:
|
|||||||
* Check basic_parser_v1 against rfc7230 for leading message whitespace
|
* Check basic_parser_v1 against rfc7230 for leading message whitespace
|
||||||
* Fix the order of message constructor parameters:
|
* Fix the order of message constructor parameters:
|
||||||
body first then headers (since body is constructed with arguments more often)
|
body first then headers (since body is constructed with arguments more often)
|
||||||
|
* Unit tests for char tables
|
||||||
|
* Remove status_code() from API when isRequest==true, et. al.
|
||||||
|
* Permit sending trailers and parameters in chunk-encoding chunks
|
||||||
|
|
||||||
|
Future:
|
||||||
|
|
||||||
|
* SOCKS proxy client and server implementations
|
||||||
|
|
||||||
|
@@ -39,6 +39,11 @@
|
|||||||
<member><link linkend="beast.ref.http__streambuf_body">streambuf_body</link></member>
|
<member><link linkend="beast.ref.http__streambuf_body">streambuf_body</link></member>
|
||||||
<member><link linkend="beast.ref.http__string_body">string_body</link></member>
|
<member><link linkend="beast.ref.http__string_body">string_body</link></member>
|
||||||
</simplelist>
|
</simplelist>
|
||||||
|
<bridgehead renderas="sect3">Options</bridgehead>
|
||||||
|
<simplelist type="vert" columns="1">
|
||||||
|
<member><link linkend="beast.ref.http__body_max_size">body_max_size</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__headers_max_size">headers_max_size</link></member>
|
||||||
|
</simplelist>
|
||||||
<bridgehead renderas="sect3">Type Traits</bridgehead>
|
<bridgehead renderas="sect3">Type Traits</bridgehead>
|
||||||
<simplelist type="vert" columns="1">
|
<simplelist type="vert" columns="1">
|
||||||
<member><link linkend="beast.ref.http__is_Body">is_Body</link></member>
|
<member><link linkend="beast.ref.http__is_Body">is_Body</link></member>
|
||||||
|
@@ -13,10 +13,78 @@
|
|||||||
namespace beast {
|
namespace beast {
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
|
enum error
|
||||||
|
{
|
||||||
|
fail_error = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
class fail_error_category : public boost::system::error_category
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const char*
|
||||||
|
name() const noexcept override
|
||||||
|
{
|
||||||
|
return "test";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
message(int ev) const override
|
||||||
|
{
|
||||||
|
switch(static_cast<error>(ev))
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case error::fail_error:
|
||||||
|
return "test error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::system::error_condition
|
||||||
|
default_error_condition(int ev) const noexcept override
|
||||||
|
{
|
||||||
|
return boost::system::error_condition{ev, *this};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
equivalent(int ev,
|
||||||
|
boost::system::error_condition const& condition
|
||||||
|
) const noexcept override
|
||||||
|
{
|
||||||
|
return condition.value() == ev &&
|
||||||
|
&condition.category() == this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
equivalent(error_code const& error, int ev) const noexcept override
|
||||||
|
{
|
||||||
|
return error.value() == ev &&
|
||||||
|
&error.category() == this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline
|
||||||
|
boost::system::error_category const&
|
||||||
|
get_error_category()
|
||||||
|
{
|
||||||
|
static fail_error_category const cat{};
|
||||||
|
return cat;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
inline
|
||||||
|
error_code
|
||||||
|
make_error_code(error ev)
|
||||||
|
{
|
||||||
|
return error_code{static_cast<int>(ev),
|
||||||
|
detail::get_error_category()};
|
||||||
|
}
|
||||||
|
|
||||||
/** A countdown to simulated failure.
|
/** A countdown to simulated failure.
|
||||||
|
|
||||||
On the Nth operation, the class will fail with the specified
|
On the Nth operation, the class will fail with the specified
|
||||||
error code, or the default error code of invalid_argument.
|
error code, or the default error code of @ref fail_error.
|
||||||
*/
|
*/
|
||||||
class fail_counter
|
class fail_counter
|
||||||
{
|
{
|
||||||
@@ -31,10 +99,10 @@ public:
|
|||||||
@param The 0-based index of the operation to fail on or after.
|
@param The 0-based index of the operation to fail on or after.
|
||||||
*/
|
*/
|
||||||
explicit
|
explicit
|
||||||
fail_counter(std::size_t n = 0)
|
fail_counter(std::size_t n,
|
||||||
|
error_code ev = make_error_code(fail_error))
|
||||||
: n_(n)
|
: n_(n)
|
||||||
, ec_(boost::system::errc::make_error_code(
|
, ec_(ev)
|
||||||
boost::system::errc::errc_t::invalid_argument))
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,4 +134,14 @@ public:
|
|||||||
} // test
|
} // test
|
||||||
} // beast
|
} // beast
|
||||||
|
|
||||||
|
namespace boost {
|
||||||
|
namespace system {
|
||||||
|
template<>
|
||||||
|
struct is_error_code_enum<beast::test::error>
|
||||||
|
{
|
||||||
|
static bool const value = true;
|
||||||
|
};
|
||||||
|
} // system
|
||||||
|
} // boost
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
#include <beast/core/detail/get_lowest_layer.hpp>
|
#include <beast/core/detail/get_lowest_layer.hpp>
|
||||||
#include <beast/websocket/teardown.hpp>
|
#include <beast/websocket/teardown.hpp>
|
||||||
#include <beast/test/fail_counter.hpp>
|
#include <beast/test/fail_counter.hpp>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace test {
|
namespace test {
|
||||||
@@ -26,8 +27,8 @@ namespace test {
|
|||||||
template<class NextLayer>
|
template<class NextLayer>
|
||||||
class fail_stream
|
class fail_stream
|
||||||
{
|
{
|
||||||
|
boost::optional<fail_counter> fc_;
|
||||||
fail_counter* pfc_;
|
fail_counter* pfc_;
|
||||||
fail_counter fc_;
|
|
||||||
NextLayer next_layer_;
|
NextLayer next_layer_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -46,8 +47,8 @@ public:
|
|||||||
template<class... Args>
|
template<class... Args>
|
||||||
explicit
|
explicit
|
||||||
fail_stream(std::size_t n, Args&&... args)
|
fail_stream(std::size_t n, Args&&... args)
|
||||||
: pfc_(&fc_)
|
: fc_(n)
|
||||||
, fc_(n)
|
, pfc_(&*fc_)
|
||||||
, next_layer_(std::forward<Args>(args)...)
|
, next_layer_(std::forward<Args>(args)...)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@@ -25,17 +25,65 @@ namespace http {
|
|||||||
namespace parse_flag {
|
namespace parse_flag {
|
||||||
enum values
|
enum values
|
||||||
{
|
{
|
||||||
chunked = 1 << 0,
|
chunked = 1,
|
||||||
connection_keep_alive = 1 << 1,
|
connection_keep_alive = 2,
|
||||||
connection_close = 1 << 2,
|
connection_close = 4,
|
||||||
connection_upgrade = 1 << 3,
|
connection_upgrade = 8,
|
||||||
trailing = 1 << 4,
|
trailing = 16,
|
||||||
upgrade = 1 << 5,
|
upgrade = 32,
|
||||||
skipbody = 1 << 6,
|
skipbody = 64,
|
||||||
contentlength = 1 << 7
|
contentlength = 128
|
||||||
};
|
};
|
||||||
} // parse_flag
|
} // parse_flag
|
||||||
|
|
||||||
|
/** Headers maximum size option.
|
||||||
|
|
||||||
|
Sets the maximum number of cumulative bytes allowed
|
||||||
|
including all header octets. A value of zero indicates
|
||||||
|
no limit on the number of header octets
|
||||||
|
|
||||||
|
The default headers maximum size is 16KB (16,384 bytes).
|
||||||
|
|
||||||
|
@note Objects of this type are passed to @ref set_option.
|
||||||
|
*/
|
||||||
|
struct headers_max_size
|
||||||
|
{
|
||||||
|
std::size_t value;
|
||||||
|
|
||||||
|
explicit
|
||||||
|
headers_max_size(std::size_t v)
|
||||||
|
: value(v)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Body maximum size option.
|
||||||
|
|
||||||
|
Sets the maximum number of cumulative bytes allowed including
|
||||||
|
all body octets. Octets in chunk-encoded bodies are counted
|
||||||
|
after decoding. A value of zero indicates no limit on
|
||||||
|
the number of body octets.
|
||||||
|
|
||||||
|
The default body maximum size for requests is 4MB (four
|
||||||
|
megabytes or 4,194,304 bytes) and unlimited for responses.
|
||||||
|
|
||||||
|
@note Objects of this type are passed to @ref set_option.
|
||||||
|
*/
|
||||||
|
struct body_max_size
|
||||||
|
{
|
||||||
|
std::size_t value;
|
||||||
|
|
||||||
|
explicit
|
||||||
|
body_max_size(std::size_t v)
|
||||||
|
: value(v)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 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();
|
||||||
|
|
||||||
/** A parser for decoding HTTP/1 wire format messages.
|
/** A parser for decoding HTTP/1 wire format messages.
|
||||||
|
|
||||||
This parser is designed to efficiently parse messages in the
|
This parser is designed to efficiently parse messages in the
|
||||||
@@ -85,7 +133,7 @@ enum values
|
|||||||
|
|
||||||
Called for each piece of the current header value.
|
Called for each piece of the current header value.
|
||||||
|
|
||||||
@li `int on_headers(error_code&)`
|
@li `int on_headers(std::uint64_t content_length, error_code&)`
|
||||||
|
|
||||||
Called when all the headers have been parsed successfully.
|
Called when all the headers have been parsed successfully.
|
||||||
|
|
||||||
@@ -126,90 +174,12 @@ enum values
|
|||||||
presented with request or response message.
|
presented with request or response message.
|
||||||
*/
|
*/
|
||||||
template<bool isRequest, class Derived>
|
template<bool isRequest, class Derived>
|
||||||
class basic_parser_v1
|
class basic_parser_v1 : public detail::parser_base
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
using self = basic_parser_v1;
|
using self = basic_parser_v1;
|
||||||
typedef void(self::*pmf_t)(error_code&, boost::string_ref const&);
|
typedef void(self::*pmf_t)(error_code&, boost::string_ref const&);
|
||||||
|
|
||||||
static std::uint64_t constexpr no_content_length =
|
|
||||||
std::numeric_limits<std::uint64_t>::max();
|
|
||||||
|
|
||||||
enum state : std::uint8_t
|
|
||||||
{
|
|
||||||
s_closed = 1,
|
|
||||||
|
|
||||||
s_req_start,
|
|
||||||
s_req_method_start,
|
|
||||||
s_req_method,
|
|
||||||
s_req_space_before_url,
|
|
||||||
s_req_url_start,
|
|
||||||
s_req_url,
|
|
||||||
s_req_http_start,
|
|
||||||
s_req_http_H,
|
|
||||||
s_req_http_HT,
|
|
||||||
s_req_http_HTT,
|
|
||||||
s_req_http_HTTP,
|
|
||||||
s_req_major_start,
|
|
||||||
s_req_major,
|
|
||||||
s_req_minor_start,
|
|
||||||
s_req_minor,
|
|
||||||
s_req_line_end,
|
|
||||||
|
|
||||||
s_res_start,
|
|
||||||
s_res_H,
|
|
||||||
s_res_HT,
|
|
||||||
s_res_HTT,
|
|
||||||
s_res_HTTP,
|
|
||||||
s_res_major_start,
|
|
||||||
s_res_major,
|
|
||||||
s_res_minor_start,
|
|
||||||
s_res_minor,
|
|
||||||
s_res_status_code_start,
|
|
||||||
s_res_status_code,
|
|
||||||
s_res_status_start,
|
|
||||||
s_res_status,
|
|
||||||
s_res_line_almost_done,
|
|
||||||
s_res_line_done,
|
|
||||||
|
|
||||||
s_header_field_start,
|
|
||||||
s_header_field,
|
|
||||||
s_header_value_start,
|
|
||||||
s_header_value_discard_lWs0,
|
|
||||||
s_header_value_discard_ws0,
|
|
||||||
s_header_value_almost_done0,
|
|
||||||
s_header_value_text_start,
|
|
||||||
s_header_value_discard_lWs,
|
|
||||||
s_header_value_discard_ws,
|
|
||||||
s_header_value_text,
|
|
||||||
s_header_value_almost_done,
|
|
||||||
|
|
||||||
s_headers_almost_done,
|
|
||||||
s_headers_done,
|
|
||||||
|
|
||||||
s_chunk_size_start,
|
|
||||||
s_chunk_size,
|
|
||||||
s_chunk_parameters,
|
|
||||||
s_chunk_size_almost_done,
|
|
||||||
|
|
||||||
// states below do not count towards
|
|
||||||
// the limit on the size of the message
|
|
||||||
|
|
||||||
s_body_identity0,
|
|
||||||
s_body_identity,
|
|
||||||
s_body_identity_eof0,
|
|
||||||
s_body_identity_eof,
|
|
||||||
|
|
||||||
s_chunk_data_start,
|
|
||||||
s_chunk_data,
|
|
||||||
s_chunk_data_almost_done,
|
|
||||||
s_chunk_data_done,
|
|
||||||
|
|
||||||
s_complete,
|
|
||||||
s_restart,
|
|
||||||
s_closed_complete
|
|
||||||
};
|
|
||||||
|
|
||||||
enum field_state : std::uint8_t
|
enum field_state : std::uint8_t
|
||||||
{
|
{
|
||||||
h_general = 0,
|
h_general = 0,
|
||||||
@@ -224,25 +194,36 @@ private:
|
|||||||
h_matching_upgrade,
|
h_matching_upgrade,
|
||||||
|
|
||||||
h_connection,
|
h_connection,
|
||||||
|
h_content_length0,
|
||||||
h_content_length,
|
h_content_length,
|
||||||
|
h_content_length_ows,
|
||||||
h_transfer_encoding,
|
h_transfer_encoding,
|
||||||
h_upgrade,
|
h_upgrade,
|
||||||
|
|
||||||
h_matching_transfer_encoding_chunked,
|
h_matching_transfer_encoding_chunked,
|
||||||
h_matching_connection_token_start,
|
h_matching_transfer_encoding_general,
|
||||||
h_matching_connection_keep_alive,
|
h_matching_connection_keep_alive,
|
||||||
h_matching_connection_close,
|
h_matching_connection_close,
|
||||||
h_matching_connection_upgrade,
|
h_matching_connection_upgrade,
|
||||||
h_matching_connection_token,
|
|
||||||
|
|
||||||
h_transfer_encoding_chunked,
|
h_transfer_encoding_chunked,
|
||||||
|
h_transfer_encoding_chunked_ows,
|
||||||
|
|
||||||
h_connection_keep_alive,
|
h_connection_keep_alive,
|
||||||
|
h_connection_keep_alive_ows,
|
||||||
h_connection_close,
|
h_connection_close,
|
||||||
|
h_connection_close_ows,
|
||||||
h_connection_upgrade,
|
h_connection_upgrade,
|
||||||
|
h_connection_upgrade_ows,
|
||||||
|
h_connection_token,
|
||||||
|
h_connection_token_ows
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::size_t h_max_;
|
||||||
|
std::size_t h_left_;
|
||||||
|
std::size_t b_max_;
|
||||||
|
std::size_t b_left_;
|
||||||
std::uint64_t content_length_;
|
std::uint64_t content_length_;
|
||||||
std::uint64_t nread_;
|
|
||||||
pmf_t cb_;
|
pmf_t cb_;
|
||||||
state s_ : 8;
|
state s_ : 8;
|
||||||
unsigned flags_ : 8;
|
unsigned flags_ : 8;
|
||||||
@@ -260,10 +241,42 @@ public:
|
|||||||
/// Copy assignment.
|
/// Copy assignment.
|
||||||
basic_parser_v1& operator=(basic_parser_v1 const&) = default;
|
basic_parser_v1& operator=(basic_parser_v1 const&) = default;
|
||||||
|
|
||||||
/// Constructor
|
/// Default constructor
|
||||||
basic_parser_v1()
|
basic_parser_v1();
|
||||||
|
|
||||||
|
/** Set options on the parser.
|
||||||
|
|
||||||
|
@param args One or more parser options to set.
|
||||||
|
*/
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
template<class... Args>
|
||||||
|
void
|
||||||
|
set_option(Args&&... args)
|
||||||
|
#else
|
||||||
|
template<class A1, class A2, class... An>
|
||||||
|
void
|
||||||
|
set_option(A1&& a1, A2&& a2, An&&... an)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
init(std::integral_constant<bool, isRequest>{});
|
set_option(std::forward<A1>(a1));
|
||||||
|
set_option(std::forward<A2>(a2),
|
||||||
|
std::forward<An>(an)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the headers maximum size option
|
||||||
|
void
|
||||||
|
set_option(headers_max_size const& o)
|
||||||
|
{
|
||||||
|
h_max_ = o.value;
|
||||||
|
h_left_ = h_max_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the body maximum size option
|
||||||
|
void
|
||||||
|
set_option(body_max_size const& o)
|
||||||
|
{
|
||||||
|
b_max_ = o.value;
|
||||||
|
b_left_ = b_max_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns internal flags associated with the parser.
|
/// Returns internal flags associated with the parser.
|
||||||
@@ -413,17 +426,48 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
init(std::true_type)
|
reset(std::true_type)
|
||||||
{
|
{
|
||||||
s_ = s_req_start;
|
s_ = s_req_start;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
init(std::false_type)
|
reset(std::false_type)
|
||||||
{
|
{
|
||||||
s_ = s_res_start;
|
s_ = s_res_start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
reset()
|
||||||
|
{
|
||||||
|
h_left_ = h_max_;
|
||||||
|
b_left_ = b_max_;
|
||||||
|
reset(std::integral_constant<bool, isRequest>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
init(std::true_type)
|
||||||
|
{
|
||||||
|
// 16KB max headers, 4MB max body
|
||||||
|
h_max_ = 16 * 1024;
|
||||||
|
b_max_ = 4 * 1024 * 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
init(std::false_type)
|
||||||
|
{
|
||||||
|
// 16KB max headers, unlimited body
|
||||||
|
h_max_ = 16 * 1024;
|
||||||
|
b_max_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
init()
|
||||||
|
{
|
||||||
|
init(std::integral_constant<bool, isRequest>{});
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
needs_eof(std::true_type) const;
|
needs_eof(std::true_type) const;
|
||||||
|
|
||||||
@@ -584,7 +628,7 @@ private:
|
|||||||
{
|
{
|
||||||
template<class T, class R = std::is_same<int,
|
template<class T, class R = std::is_same<int,
|
||||||
decltype(std::declval<T>().on_headers(
|
decltype(std::declval<T>().on_headers(
|
||||||
std::declval<error_code&>()))>>
|
std::declval<std::uint64_t>(), std::declval<error_code&>()))>>
|
||||||
static R check(int);
|
static R check(int);
|
||||||
template <class>
|
template <class>
|
||||||
static std::false_type check(...);
|
static std::false_type check(...);
|
||||||
@@ -661,8 +705,16 @@ private:
|
|||||||
void call_on_method(error_code& ec,
|
void call_on_method(error_code& ec,
|
||||||
boost::string_ref const& s)
|
boost::string_ref const& s)
|
||||||
{
|
{
|
||||||
call_on_method(ec, s, std::integral_constant<bool,
|
if(! h_max_ || s.size() <= h_left_)
|
||||||
isRequest && has_on_method<Derived>::value>{});
|
{
|
||||||
|
h_left_ -= s.size();
|
||||||
|
call_on_method(ec, s, std::integral_constant<bool,
|
||||||
|
isRequest && has_on_method<Derived>::value>{});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ec = parse_error::headers_too_big;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void call_on_uri(error_code& ec,
|
void call_on_uri(error_code& ec,
|
||||||
@@ -678,8 +730,16 @@ 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)
|
||||||
{
|
{
|
||||||
call_on_uri(ec, s, std::integral_constant<bool,
|
if(! h_max_ || s.size() <= h_left_)
|
||||||
isRequest && has_on_uri<Derived>::value>{});
|
{
|
||||||
|
h_left_ -= s.size();
|
||||||
|
call_on_uri(ec, s, std::integral_constant<bool,
|
||||||
|
isRequest && has_on_uri<Derived>::value>{});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ec = parse_error::headers_too_big;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void call_on_reason(error_code& ec,
|
void call_on_reason(error_code& ec,
|
||||||
@@ -695,8 +755,16 @@ private:
|
|||||||
|
|
||||||
void call_on_reason(error_code& ec, boost::string_ref const& s)
|
void call_on_reason(error_code& ec, boost::string_ref const& s)
|
||||||
{
|
{
|
||||||
call_on_reason(ec, s, std::integral_constant<bool,
|
if(! h_max_ || s.size() <= h_left_)
|
||||||
! isRequest && has_on_reason<Derived>::value>{});
|
{
|
||||||
|
h_left_ -= s.size();
|
||||||
|
call_on_reason(ec, s, std::integral_constant<bool,
|
||||||
|
! isRequest && has_on_reason<Derived>::value>{});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ec = parse_error::headers_too_big;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void call_on_request(error_code& ec, std::true_type)
|
void call_on_request(error_code& ec, std::true_type)
|
||||||
@@ -742,7 +810,15 @@ private:
|
|||||||
|
|
||||||
void call_on_field(error_code& ec, boost::string_ref const& s)
|
void call_on_field(error_code& ec, boost::string_ref const& s)
|
||||||
{
|
{
|
||||||
call_on_field(ec, s, has_on_field<Derived>{});
|
if(! h_max_ || s.size() <= h_left_)
|
||||||
|
{
|
||||||
|
h_left_ -= s.size();
|
||||||
|
call_on_field(ec, s, has_on_field<Derived>{});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ec = parse_error::headers_too_big;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void call_on_value(error_code& ec,
|
void call_on_value(error_code& ec,
|
||||||
@@ -758,22 +834,32 @@ private:
|
|||||||
|
|
||||||
void call_on_value(error_code& ec, boost::string_ref const& s)
|
void call_on_value(error_code& ec, boost::string_ref const& s)
|
||||||
{
|
{
|
||||||
call_on_value(ec, s, has_on_value<Derived>{});
|
if(! h_max_ || s.size() <= h_left_)
|
||||||
|
{
|
||||||
|
h_left_ -= s.size();
|
||||||
|
call_on_value(ec, s, has_on_value<Derived>{});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ec = parse_error::headers_too_big;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int call_on_headers(error_code& ec, std::true_type)
|
int call_on_headers(error_code& ec,
|
||||||
|
std::uint64_t content_length, std::true_type)
|
||||||
{
|
{
|
||||||
return impl().on_headers(ec);
|
return impl().on_headers(content_length, ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
int call_on_headers(error_code& ec, std::false_type)
|
int call_on_headers(error_code& ec, std::uint64_t, std::false_type)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int call_on_headers(error_code& ec)
|
int call_on_headers(error_code& ec)
|
||||||
{
|
{
|
||||||
return call_on_headers(ec, has_on_headers<Derived>{});
|
return call_on_headers(ec, content_length_,
|
||||||
|
has_on_headers<Derived>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
void call_on_body(error_code& ec,
|
void call_on_body(error_code& ec,
|
||||||
@@ -789,7 +875,15 @@ private:
|
|||||||
|
|
||||||
void call_on_body(error_code& ec, boost::string_ref const& s)
|
void call_on_body(error_code& ec, boost::string_ref const& s)
|
||||||
{
|
{
|
||||||
call_on_body(ec, s, has_on_body<Derived>{});
|
if(! b_max_ || s.size() <= b_left_)
|
||||||
|
{
|
||||||
|
b_left_ -= s.size();
|
||||||
|
call_on_body(ec, s, has_on_body<Derived>{});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ec = parse_error::body_too_big;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void call_on_complete(error_code& ec, std::true_type)
|
void call_on_complete(error_code& ec, std::true_type)
|
||||||
|
@@ -17,137 +17,6 @@ namespace beast {
|
|||||||
namespace http {
|
namespace http {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
// '0'...'9'
|
|
||||||
inline
|
|
||||||
bool
|
|
||||||
is_digit(char c)
|
|
||||||
{
|
|
||||||
return c >= '0' && c <= '9';
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
bool
|
|
||||||
is_token(char c)
|
|
||||||
{
|
|
||||||
/* token = 1*<any CHAR except CTLs or separators>
|
|
||||||
CHAR = <any US-ASCII character (octets 0 - 127)>
|
|
||||||
sep = "(" | ")" | "<" | ">" | "@"
|
|
||||||
| "," | ";" | ":" | "\" | <">
|
|
||||||
| "/" | "[" | "]" | "?" | "="
|
|
||||||
| "{" | "}" | SP | HT
|
|
||||||
*/
|
|
||||||
static std::array<char, 256> constexpr tab = {{
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
|
||||||
0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48
|
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, // 112
|
|
||||||
}};
|
|
||||||
return tab[static_cast<std::uint8_t>(c)] != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
bool
|
|
||||||
is_text(char c)
|
|
||||||
{
|
|
||||||
// TEXT = <any OCTET except CTLs, but including LWS>
|
|
||||||
static std::array<char, 256> constexpr tab = {{
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
|
|
||||||
}};
|
|
||||||
return tab[static_cast<std::uint8_t>(c)] != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// converts to lower case,
|
|
||||||
// returns 0 if not a valid token char
|
|
||||||
//
|
|
||||||
inline
|
|
||||||
char
|
|
||||||
to_field_char(char c)
|
|
||||||
{
|
|
||||||
/* token = 1*<any CHAR except CTLs or separators>
|
|
||||||
CHAR = <any US-ASCII character (octets 0 - 127)>
|
|
||||||
sep = "(" | ")" | "<" | ">" | "@"
|
|
||||||
| "," | ";" | ":" | "\" | <">
|
|
||||||
| "/" | "[" | "]" | "?" | "="
|
|
||||||
| "{" | "}" | SP | HT
|
|
||||||
*/
|
|
||||||
static std::array<char, 256> constexpr tab = {{
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, '!', 0, '#', '$', '%', '&', '\'', 0, 0, '*', '+', 0, '-', '.', 0,
|
|
||||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
|
||||||
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, '^', '_',
|
|
||||||
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
|
||||||
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, '|', 0, '~', 0
|
|
||||||
}};
|
|
||||||
return tab[static_cast<std::uint8_t>(c)];
|
|
||||||
}
|
|
||||||
|
|
||||||
// converts to lower case,
|
|
||||||
// returns 0 if not a valid text char
|
|
||||||
//
|
|
||||||
inline
|
|
||||||
char
|
|
||||||
to_value_char(char c)
|
|
||||||
{
|
|
||||||
// TEXT = <any OCTET except CTLs, but including LWS>
|
|
||||||
static std::array<std::uint8_t, 256> constexpr tab = {{
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, // 0
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
|
||||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 32
|
|
||||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 48
|
|
||||||
64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 64
|
|
||||||
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, // 80
|
|
||||||
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 96
|
|
||||||
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 0, // 112
|
|
||||||
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, // 128
|
|
||||||
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 144
|
|
||||||
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, // 160
|
|
||||||
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, // 176
|
|
||||||
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, // 192
|
|
||||||
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, // 208
|
|
||||||
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 224
|
|
||||||
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 // 240
|
|
||||||
}};
|
|
||||||
return static_cast<char>(tab[static_cast<std::uint8_t>(c)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
std::int8_t
|
|
||||||
unhex(char c)
|
|
||||||
{
|
|
||||||
static std::array<std::int8_t, 256> constexpr tab = {{
|
|
||||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0
|
|
||||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16
|
|
||||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32
|
|
||||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 48
|
|
||||||
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 64
|
|
||||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80
|
|
||||||
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 96
|
|
||||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 112
|
|
||||||
}};
|
|
||||||
return tab[static_cast<std::uint8_t>(c)];
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class = void>
|
template<class = void>
|
||||||
struct parser_str_t
|
struct parser_str_t
|
||||||
{
|
{
|
||||||
@@ -196,6 +65,82 @@ parser_str_t<_>::transfer_encoding[18];
|
|||||||
|
|
||||||
using parser_str = parser_str_t<>;
|
using parser_str = parser_str_t<>;
|
||||||
|
|
||||||
|
class parser_base
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
enum state : std::uint8_t
|
||||||
|
{
|
||||||
|
s_dead = 1,
|
||||||
|
|
||||||
|
s_req_start,
|
||||||
|
s_req_method0,
|
||||||
|
s_req_method,
|
||||||
|
s_req_url0,
|
||||||
|
s_req_url,
|
||||||
|
s_req_http,
|
||||||
|
s_req_http_H,
|
||||||
|
s_req_http_HT,
|
||||||
|
s_req_http_HTT,
|
||||||
|
s_req_http_HTTP,
|
||||||
|
s_req_major,
|
||||||
|
s_req_dot,
|
||||||
|
s_req_minor,
|
||||||
|
s_req_cr,
|
||||||
|
s_req_lf,
|
||||||
|
|
||||||
|
s_res_start,
|
||||||
|
s_res_H,
|
||||||
|
s_res_HT,
|
||||||
|
s_res_HTT,
|
||||||
|
s_res_HTTP,
|
||||||
|
s_res_major,
|
||||||
|
s_res_dot,
|
||||||
|
s_res_minor,
|
||||||
|
s_res_space_1,
|
||||||
|
s_res_status0,
|
||||||
|
s_res_status1,
|
||||||
|
s_res_status2,
|
||||||
|
s_res_space_2,
|
||||||
|
s_res_reason0,
|
||||||
|
s_res_reason,
|
||||||
|
s_res_line_lf,
|
||||||
|
s_res_line_done,
|
||||||
|
|
||||||
|
s_header_name0,
|
||||||
|
s_header_name,
|
||||||
|
s_header_value0_lf,
|
||||||
|
s_header_value0_almost_done,
|
||||||
|
s_header_value0,
|
||||||
|
s_header_value,
|
||||||
|
s_header_value_lf,
|
||||||
|
s_header_value_almost_done,
|
||||||
|
s_header_value_unfold,
|
||||||
|
|
||||||
|
s_headers_almost_done,
|
||||||
|
s_headers_done,
|
||||||
|
|
||||||
|
s_chunk_size0,
|
||||||
|
s_chunk_size,
|
||||||
|
s_chunk_ext_name0,
|
||||||
|
s_chunk_ext_name,
|
||||||
|
s_chunk_ext_val,
|
||||||
|
s_chunk_size_lf,
|
||||||
|
s_chunk_data0,
|
||||||
|
s_chunk_data,
|
||||||
|
s_chunk_data_cr,
|
||||||
|
s_chunk_data_lf,
|
||||||
|
|
||||||
|
s_body_identity0,
|
||||||
|
s_body_identity,
|
||||||
|
s_body_identity_eof0,
|
||||||
|
s_body_identity_eof,
|
||||||
|
|
||||||
|
s_complete,
|
||||||
|
s_restart,
|
||||||
|
s_closed_complete
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
} // http
|
} // http
|
||||||
} // beast
|
} // beast
|
||||||
|
@@ -17,6 +17,56 @@ namespace beast {
|
|||||||
namespace http {
|
namespace http {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
is_digit(char c)
|
||||||
|
{
|
||||||
|
return c >= '0' && c <= '9';
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
is_alpha(char c)
|
||||||
|
{
|
||||||
|
static std::array<char, 256> constexpr tab = {{
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 48
|
||||||
|
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 80
|
||||||
|
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 112
|
||||||
|
}};
|
||||||
|
return tab[static_cast<std::uint8_t>(c)] != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
is_text(char c)
|
||||||
|
{
|
||||||
|
// TEXT = <any OCTET except CTLs, but including LWS>
|
||||||
|
static std::array<char, 256> constexpr tab = {{
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
|
||||||
|
}};
|
||||||
|
return tab[static_cast<std::uint8_t>(c)] != 0;
|
||||||
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
bool
|
bool
|
||||||
is_tchar(char c)
|
is_tchar(char c)
|
||||||
@@ -27,7 +77,7 @@ is_tchar(char c)
|
|||||||
"^" | "_" | "`" | "|" | "~" |
|
"^" | "_" | "`" | "|" | "~" |
|
||||||
DIGIT | ALPHA
|
DIGIT | ALPHA
|
||||||
*/
|
*/
|
||||||
static std::array<bool, 256> constexpr tab = {{
|
static std::array<char, 256> constexpr tab = {{
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||||
0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32
|
0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32
|
||||||
@@ -37,7 +87,7 @@ is_tchar(char c)
|
|||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, // 112
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, // 112
|
||||||
}};
|
}};
|
||||||
return tab[static_cast<std::uint8_t>(c)];
|
return tab[static_cast<std::uint8_t>(c)] != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
@@ -97,6 +147,79 @@ is_qpchar(char c)
|
|||||||
return tab[static_cast<std::uint8_t>(c)];
|
return tab[static_cast<std::uint8_t>(c)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// converts to lower case,
|
||||||
|
// returns 0 if not a valid token char
|
||||||
|
//
|
||||||
|
inline
|
||||||
|
char
|
||||||
|
to_field_char(char c)
|
||||||
|
{
|
||||||
|
/* token = 1*<any CHAR except CTLs or separators>
|
||||||
|
CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||||
|
sep = "(" | ")" | "<" | ">" | "@"
|
||||||
|
| "," | ";" | ":" | "\" | <">
|
||||||
|
| "/" | "[" | "]" | "?" | "="
|
||||||
|
| "{" | "}" | SP | HT
|
||||||
|
*/
|
||||||
|
static std::array<char, 256> constexpr tab = {{
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, '!', 0, '#', '$', '%', '&', '\'', 0, 0, '*', '+', 0, '-', '.', 0,
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
||||||
|
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, '^', '_',
|
||||||
|
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
||||||
|
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, '|', 0, '~', 0
|
||||||
|
}};
|
||||||
|
return tab[static_cast<std::uint8_t>(c)];
|
||||||
|
}
|
||||||
|
|
||||||
|
// converts to lower case,
|
||||||
|
// returns 0 if not a valid text char
|
||||||
|
//
|
||||||
|
inline
|
||||||
|
char
|
||||||
|
to_value_char(char c)
|
||||||
|
{
|
||||||
|
// TEXT = <any OCTET except CTLs, but including LWS>
|
||||||
|
static std::array<std::uint8_t, 256> constexpr tab = {{
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, // 0
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||||
|
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 32
|
||||||
|
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 48
|
||||||
|
64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 64
|
||||||
|
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, // 80
|
||||||
|
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 96
|
||||||
|
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 0, // 112
|
||||||
|
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, // 128
|
||||||
|
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 144
|
||||||
|
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, // 160
|
||||||
|
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, // 176
|
||||||
|
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, // 192
|
||||||
|
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, // 208
|
||||||
|
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 224
|
||||||
|
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 // 240
|
||||||
|
}};
|
||||||
|
return static_cast<char>(tab[static_cast<std::uint8_t>(c)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
std::int8_t
|
||||||
|
unhex(char c)
|
||||||
|
{
|
||||||
|
static std::array<std::int8_t, 256> constexpr tab = {{
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 48
|
||||||
|
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 64
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80
|
||||||
|
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 96
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 112
|
||||||
|
}};
|
||||||
|
return tab[static_cast<std::uint8_t>(c)];
|
||||||
|
}
|
||||||
|
|
||||||
template<class FwdIt>
|
template<class FwdIt>
|
||||||
void
|
void
|
||||||
skip_ows(FwdIt& it, FwdIt const& end)
|
skip_ows(FwdIt& it, FwdIt const& end)
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -23,8 +23,8 @@ enum class parse_error
|
|||||||
bad_crlf,
|
bad_crlf,
|
||||||
bad_request,
|
bad_request,
|
||||||
|
|
||||||
bad_status_code,
|
|
||||||
bad_status,
|
bad_status,
|
||||||
|
bad_reason,
|
||||||
|
|
||||||
bad_field,
|
bad_field,
|
||||||
bad_value,
|
bad_value,
|
||||||
@@ -33,7 +33,11 @@ enum class parse_error
|
|||||||
bad_on_headers_rv,
|
bad_on_headers_rv,
|
||||||
|
|
||||||
invalid_chunk_size,
|
invalid_chunk_size,
|
||||||
|
invalid_ext_name,
|
||||||
|
invalid_ext_val,
|
||||||
|
|
||||||
|
headers_too_big,
|
||||||
|
body_too_big,
|
||||||
short_read,
|
short_read,
|
||||||
|
|
||||||
general
|
general
|
||||||
@@ -60,7 +64,7 @@ public:
|
|||||||
return "bad method";
|
return "bad method";
|
||||||
|
|
||||||
case parse_error::bad_uri:
|
case parse_error::bad_uri:
|
||||||
return "bad Request-URI";
|
return "bad request-target";
|
||||||
|
|
||||||
case parse_error::bad_version:
|
case parse_error::bad_version:
|
||||||
return "bad HTTP-Version";
|
return "bad HTTP-Version";
|
||||||
@@ -69,13 +73,13 @@ public:
|
|||||||
return "missing CRLF";
|
return "missing CRLF";
|
||||||
|
|
||||||
case parse_error::bad_request:
|
case parse_error::bad_request:
|
||||||
return "bad Request-Line";
|
return "bad reason-phrase";
|
||||||
|
|
||||||
case parse_error::bad_status_code:
|
|
||||||
return "bad Status-Code";
|
|
||||||
|
|
||||||
case parse_error::bad_status:
|
case parse_error::bad_status:
|
||||||
return "bad Status-Line";
|
return "bad status-code";
|
||||||
|
|
||||||
|
case parse_error::bad_reason:
|
||||||
|
return "bad reason-phrase";
|
||||||
|
|
||||||
case parse_error::bad_field:
|
case parse_error::bad_field:
|
||||||
return "bad field token";
|
return "bad field token";
|
||||||
@@ -95,6 +99,18 @@ public:
|
|||||||
case parse_error::invalid_chunk_size:
|
case parse_error::invalid_chunk_size:
|
||||||
return "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";
|
||||||
|
|
||||||
|
case parse_error::headers_too_big:
|
||||||
|
return "headers size limit exceeded";
|
||||||
|
|
||||||
|
case parse_error::body_too_big:
|
||||||
|
return "body size limit exceeded";
|
||||||
|
|
||||||
case parse_error::short_read:
|
case parse_error::short_read:
|
||||||
return "unexpected end of data";
|
return "unexpected end of data";
|
||||||
|
|
||||||
|
@@ -172,7 +172,7 @@ private:
|
|||||||
m_.reason = std::move(this->reason_);
|
m_.reason = std::move(this->reason_);
|
||||||
}
|
}
|
||||||
|
|
||||||
int on_headers(error_code&)
|
int on_headers(std::uint64_t, error_code&)
|
||||||
{
|
{
|
||||||
flush();
|
flush();
|
||||||
m_.version = 10 * this->http_major() + this->http_minor();
|
m_.version = 10 * this->http_major() + this->http_minor();
|
||||||
|
@@ -6,6 +6,8 @@ GroupSources(test/http "/")
|
|||||||
|
|
||||||
add_executable (http-tests
|
add_executable (http-tests
|
||||||
${BEAST_INCLUDES}
|
${BEAST_INCLUDES}
|
||||||
|
message_fuzz.hpp
|
||||||
|
fail_parser.hpp
|
||||||
../../extras/beast/unit_test/main.cpp
|
../../extras/beast/unit_test/main.cpp
|
||||||
basic_dynabuf_body.cpp
|
basic_dynabuf_body.cpp
|
||||||
basic_headers.cpp
|
basic_headers.cpp
|
||||||
@@ -35,6 +37,7 @@ endif()
|
|||||||
|
|
||||||
add_executable (bench-tests
|
add_executable (bench-tests
|
||||||
${BEAST_INCLUDES}
|
${BEAST_INCLUDES}
|
||||||
|
nodejs_parser.hpp
|
||||||
../../extras/beast/unit_test/main.cpp
|
../../extras/beast/unit_test/main.cpp
|
||||||
nodejs_parser.cpp
|
nodejs_parser.cpp
|
||||||
parser_bench.cpp
|
parser_bench.cpp
|
||||||
|
File diff suppressed because it is too large
Load Diff
112
test/http/fail_parser.hpp
Normal file
112
test/http/fail_parser.hpp
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_TEST_FAIL_PARSER_HPP
|
||||||
|
#define BEAST_HTTP_TEST_FAIL_PARSER_HPP
|
||||||
|
|
||||||
|
#include <beast/http/basic_parser_v1.hpp>
|
||||||
|
#include <beast/test/fail_counter.hpp>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
template<bool isRequest>
|
||||||
|
class fail_parser
|
||||||
|
: public basic_parser_v1<isRequest, fail_parser<isRequest>>
|
||||||
|
{
|
||||||
|
test::fail_counter& fc_;
|
||||||
|
std::uint64_t content_length_ = no_content_length;
|
||||||
|
int body_rv_ = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::string body;
|
||||||
|
|
||||||
|
template<class... Args>
|
||||||
|
explicit
|
||||||
|
fail_parser(test::fail_counter& fc, Args&&... args)
|
||||||
|
: fc_(fc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
on_body_rv(int rv)
|
||||||
|
{
|
||||||
|
body_rv_ = rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// valid on successful parse
|
||||||
|
std::uint64_t
|
||||||
|
content_length() const
|
||||||
|
{
|
||||||
|
return content_length_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_start(error_code& ec)
|
||||||
|
{
|
||||||
|
fc_.fail(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_method(boost::string_ref const&, error_code& ec)
|
||||||
|
{
|
||||||
|
fc_.fail(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_uri(boost::string_ref const&, error_code& ec)
|
||||||
|
{
|
||||||
|
fc_.fail(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_reason(boost::string_ref const&, error_code& ec)
|
||||||
|
{
|
||||||
|
fc_.fail(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_request(error_code& ec)
|
||||||
|
{
|
||||||
|
fc_.fail(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_response(error_code& ec)
|
||||||
|
{
|
||||||
|
fc_.fail(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_field(boost::string_ref const&, error_code& ec)
|
||||||
|
{
|
||||||
|
fc_.fail(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_value(boost::string_ref const&, error_code& ec)
|
||||||
|
{
|
||||||
|
fc_.fail(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
int on_headers(std::uint64_t content_length, error_code& ec)
|
||||||
|
{
|
||||||
|
if(fc_.fail(ec))
|
||||||
|
return 0;
|
||||||
|
content_length_ = content_length;
|
||||||
|
return body_rv_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_body(boost::string_ref const& s, error_code& ec)
|
||||||
|
{
|
||||||
|
if(fc_.fail(ec))
|
||||||
|
return;
|
||||||
|
body.append(s.data(), s.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_complete(error_code& ec)
|
||||||
|
{
|
||||||
|
fc_.fail(ec);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
@@ -38,8 +38,8 @@ public:
|
|||||||
check("http", parse_error::bad_version);
|
check("http", parse_error::bad_version);
|
||||||
check("http", parse_error::bad_crlf);
|
check("http", parse_error::bad_crlf);
|
||||||
check("http", parse_error::bad_request);
|
check("http", parse_error::bad_request);
|
||||||
check("http", parse_error::bad_status_code);
|
|
||||||
check("http", parse_error::bad_status);
|
check("http", parse_error::bad_status);
|
||||||
|
check("http", parse_error::bad_reason);
|
||||||
check("http", parse_error::bad_field);
|
check("http", parse_error::bad_field);
|
||||||
check("http", parse_error::bad_value);
|
check("http", parse_error::bad_value);
|
||||||
check("http", parse_error::bad_content_length);
|
check("http", parse_error::bad_content_length);
|
||||||
|
@@ -8,6 +8,8 @@
|
|||||||
// Test that header file is self-contained.
|
// Test that header file is self-contained.
|
||||||
#include <beast/http/read.hpp>
|
#include <beast/http/read.hpp>
|
||||||
|
|
||||||
|
#include "fail_parser.hpp"
|
||||||
|
|
||||||
#include <beast/http/headers.hpp>
|
#include <beast/http/headers.hpp>
|
||||||
#include <beast/http/streambuf_body.hpp>
|
#include <beast/http/streambuf_body.hpp>
|
||||||
#include <beast/test/fail_stream.hpp>
|
#include <beast/test/fail_stream.hpp>
|
||||||
@@ -24,77 +26,6 @@ class read_test
|
|||||||
, public test::enable_yield_to
|
, public test::enable_yield_to
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
template<bool isRequest>
|
|
||||||
class fail_parser
|
|
||||||
: public basic_parser_v1<isRequest, fail_parser<isRequest>>
|
|
||||||
{
|
|
||||||
test::fail_counter& fc_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
template<class... Args>
|
|
||||||
explicit
|
|
||||||
fail_parser(test::fail_counter& fc, Args&&... args)
|
|
||||||
: fc_(fc)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_start(error_code& ec)
|
|
||||||
{
|
|
||||||
fc_.fail(ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_method(boost::string_ref const&, error_code& ec)
|
|
||||||
{
|
|
||||||
fc_.fail(ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_uri(boost::string_ref const&, error_code& ec)
|
|
||||||
{
|
|
||||||
fc_.fail(ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_reason(boost::string_ref const&, error_code& ec)
|
|
||||||
{
|
|
||||||
fc_.fail(ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_request(error_code& ec)
|
|
||||||
{
|
|
||||||
fc_.fail(ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_response(error_code& ec)
|
|
||||||
{
|
|
||||||
fc_.fail(ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_field(boost::string_ref const&, error_code& ec)
|
|
||||||
{
|
|
||||||
fc_.fail(ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_value(boost::string_ref const&, error_code& ec)
|
|
||||||
{
|
|
||||||
fc_.fail(ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
int on_headers(error_code& ec)
|
|
||||||
{
|
|
||||||
fc_.fail(ec);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_body(boost::string_ref const&, error_code& ec)
|
|
||||||
{
|
|
||||||
fc_.fail(ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_complete(error_code& ec)
|
|
||||||
{
|
|
||||||
fc_.fail(ec);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<bool isRequest>
|
template<bool isRequest>
|
||||||
void failMatrix(const char* s, yield_context do_yield)
|
void failMatrix(const char* s, yield_context do_yield)
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user