Add overload for basic_fields::insert() to set error_code

This commit is contained in:
Mohammad Nejati
2024-08-16 22:40:53 +00:00
committed by Mohammad Nejati
parent 848e20680f
commit 2e6d9bb491
6 changed files with 215 additions and 67 deletions

View File

@ -164,7 +164,13 @@ enum class error
unexpected end-of-file condition is encountered while trying
to read from the file.
*/
short_read
short_read,
/// Header field name exceeds @ref basic_fields::max_name_size.
header_field_name_too_large,
/// Header field value exceeds @ref basic_fields::max_value_size.
header_field_value_too_large
};
} // http

View File

@ -10,9 +10,10 @@
#ifndef BOOST_BEAST_HTTP_FIELDS_HPP
#define BOOST_BEAST_HTTP_FIELDS_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/string.hpp>
#include <boost/beast/core/detail/allocator.hpp>
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/beast/core/string.hpp>
#include <boost/beast/http/field.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/core/empty_value.hpp>
@ -218,6 +219,14 @@ private:
public:
/// Maximum field name size
static std::size_t constexpr max_name_size =
(std::numeric_limits<std::uint16_t>::max)() - 2;
/// Maximum field value size
static std::size_t constexpr max_value_size =
(std::numeric_limits<std::uint16_t>::max)() - 2;
/// Destructor
~basic_fields();
@ -434,13 +443,15 @@ public:
@param name The field name.
@param value The value of the field, as a @ref boost::beast::string_view
@param value The field value.
@throws boost::system::system_error Thrown if an error occurs:
@li If the size of @c value exceeds @ref max_value_size, the
error code will be @ref error::header_field_value_too_large.
*/
void
insert(field name, string_view const& value);
insert(field name, string_view value);
/* Set a field from a null pointer (deleted).
*/
void
insert(field, std::nullptr_t) = delete;
@ -453,13 +464,17 @@ public:
@param name The field name. It is interpreted as a case-insensitive string.
@param value The value of the field, as a @ref boost::beast::string_view
@param value The field value.
@throws boost::system::system_error Thrown if an error occurs:
@li If the size of @c name exceeds @ref max_name_size, the
error code will be @ref error::header_field_name_too_large.
@li If the size of @c value exceeds @ref max_value_size, the
error code will be @ref error::header_field_value_too_large.
*/
void
insert(string_view name, string_view const& value);
insert(string_view name, string_view value);
/* Insert a field from a null pointer (deleted).
*/
void
insert(string_view, std::nullptr_t) = delete;
@ -477,15 +492,50 @@ public:
must be equal to `to_string(name)` using a case-insensitive
comparison, otherwise the behavior is undefined.
@param value The value of the field, as a @ref boost::beast::string_view
@param value The field value.
@throws boost::system::system_error Thrown if an error occurs:
@li If the size of @c name_string exceeds @ref max_name_size,
the error code will be @ref error::header_field_name_too_large.
@li If the size of @c value exceeds @ref max_value_size, the
error code will be @ref error::header_field_value_too_large.
*/
void
insert(field name, string_view name_string,
string_view const& value);
string_view value);
void
insert(field, string_view, std::nullptr_t) = delete;
/** Insert a field.
If one or more fields with the same name already exist,
the new field will be inserted after the last field with
the matching name, in serialization order.
The value can be an empty string.
@param name The field name.
@param name_string The literal text corresponding to the
field name. If `name != field::unknown`, then this value
must be equal to `to_string(name)` using a case-insensitive
comparison, otherwise the behavior is undefined.
@param value The field value.
@param ec Set to indicate what error occurred:
@li If the size of @c name_string exceeds @ref max_name_size,
the error code will be @ref error::header_field_name_too_large.
@li If the size of @c value exceeds @ref max_value_size, the
error code will be @ref error::header_field_value_too_large.
*/
void
insert(field name, string_view name_string,
string_view value, error_code& ec);
void
insert(field, string_view, std::nullptr_t, error_code& ec) = delete;
/** Set a field value, removing any other instances of that field.
First removes any values with matching field names, then
@ -493,13 +543,14 @@ public:
@param name The field name.
@param value The value of the field, as a @ref boost::beast::string_view
@return The field value.
@param value The field value.
@throws boost::system::system_error Thrown if an error occurs:
@li If the size of @c value exceeds @ref max_value_size, the
error code will be @ref error::header_field_value_too_large.
*/
void
set(field name, string_view const& value);
set(field name, string_view value);
void
set(field, std::nullptr_t) = delete;
@ -511,10 +562,16 @@ public:
@param name The field name. It is interpreted as a case-insensitive string.
@param value The value of the field, as a @ref boost::beast::string_view
@param value The field value.
@throws boost::system::system_error Thrown if an error occurs:
@li If the size of @c name exceeds @ref max_name_size, the
error code will be @ref error::header_field_name_too_large.
@li If the size of @c value exceeds @ref max_value_size, the
error code will be @ref error::header_field_value_too_large.
*/
void
set(string_view name, string_view const& value);
set(string_view name, string_view value);
void
set(string_view, std::nullptr_t) = delete;
@ -744,9 +801,21 @@ private:
template<class OtherAlloc>
friend class basic_fields;
element*
try_create_new_element(
field name,
string_view sname,
string_view value,
error_code& ec);
element&
new_element(field name,
string_view sname, string_view value);
new_element(
field name,
string_view sname,
string_view value);
void
insert_element(element& e);
void
delete_element(element& e);

View File

@ -61,6 +61,8 @@ public:
case error::multiple_content_length: return "multiple Content-Length";
case error::stale_parser: return "stale parser";
case error::short_read: return "unexpected eof in body";
case error::header_field_name_too_large: return "header field name too large";
case error::header_field_value_too_large: return "header field value too large";
default:
return "beast.http error";

View File

@ -537,7 +537,7 @@ template<class Allocator>
inline
void
basic_fields<Allocator>::
insert(field name, string_view const& value)
insert(field name, string_view value)
{
BOOST_ASSERT(name != field::unknown);
insert(name, to_string(name), value);
@ -546,58 +546,52 @@ insert(field name, string_view const& value)
template<class Allocator>
void
basic_fields<Allocator>::
insert(string_view sname, string_view const& value)
insert(string_view sname, string_view value)
{
auto const name =
string_to_field(sname);
insert(name, sname, value);
insert(
string_to_field(sname), sname, value);
}
template<class Allocator>
void
basic_fields<Allocator>::
insert(
field name,
string_view sname,
string_view value,
error_code& ec)
{
ec = {};
auto* e = try_create_new_element(name, sname, value, ec);
if(ec.failed())
return;
insert_element(*e);
}
template<class Allocator>
void
basic_fields<Allocator>::
insert(field name,
string_view sname, string_view const& value)
string_view sname, string_view value)
{
auto& e = new_element(name, sname,
static_cast<string_view>(value));
auto const before =
set_.upper_bound(sname, key_compare{});
if(before == set_.begin())
{
BOOST_ASSERT(count(sname) == 0);
set_.insert_before(before, e);
list_.push_back(e);
return;
}
auto const last = std::prev(before);
// VFALCO is it worth comparing `field name` first?
if(! beast::iequals(sname, last->name_string()))
{
BOOST_ASSERT(count(sname) == 0);
set_.insert_before(before, e);
list_.push_back(e);
return;
}
// keep duplicate fields together in the list
set_.insert_before(before, e);
list_.insert(++list_.iterator_to(*last), e);
insert_element(
new_element(name, sname, value));
}
template<class Allocator>
void
basic_fields<Allocator>::
set(field name, string_view const& value)
set(field name, string_view value)
{
BOOST_ASSERT(name != field::unknown);
set_element(new_element(name, to_string(name),
static_cast<string_view>(value)));
set_element(
new_element(name, to_string(name), value));
}
template<class Allocator>
void
basic_fields<Allocator>::
set(string_view sname, string_view const& value)
set(string_view sname, string_view value)
{
set_element(new_element(
string_to_field(sname), sname, value));
@ -953,18 +947,22 @@ set_keep_alive_impl(
template<class Allocator>
auto
basic_fields<Allocator>::
new_element(field name,
string_view sname, string_view value) ->
element&
try_create_new_element(
field name,
string_view sname,
string_view value,
error_code& ec) -> element*
{
if(sname.size() + 2 >
(std::numeric_limits<off_t>::max)())
BOOST_THROW_EXCEPTION(std::length_error{
"field name too large"});
if(value.size() + 2 >
(std::numeric_limits<off_t>::max)())
BOOST_THROW_EXCEPTION(std::length_error{
"field value too large"});
if(sname.size() > max_name_size)
{
BOOST_BEAST_ASSIGN_EC(ec, error::header_field_name_too_large);
return nullptr;
}
if(value.size() > max_value_size)
{
BOOST_BEAST_ASSIGN_EC(ec, error::header_field_value_too_large);
return nullptr;
}
value = detail::trim(value);
std::uint16_t const off =
static_cast<off_t>(sname.size() + 2);
@ -974,7 +972,50 @@ new_element(field name,
auto const p = alloc_traits::allocate(a,
(sizeof(element) + off + len + 2 + sizeof(align_type) - 1) /
sizeof(align_type));
return *(::new(p) element(name, sname, value));
return ::new(p) element(name, sname, value);
}
template<class Allocator>
auto
basic_fields<Allocator>::
new_element(
field name,
string_view sname,
string_view value) -> element&
{
error_code ec;
auto* e = try_create_new_element(name, sname, value, ec);
if(ec.failed())
BOOST_THROW_EXCEPTION(system_error{ec});
return *e;
}
template<class Allocator>
void
basic_fields<Allocator>::
insert_element(element& e)
{
auto const before =
set_.upper_bound(e.name_string(), key_compare{});
if(before == set_.begin())
{
BOOST_ASSERT(count(e.name_string()) == 0);
set_.insert_before(before, e);
list_.push_back(e);
return;
}
auto const last = std::prev(before);
// VFALCO is it worth comparing `field name` first?
if(! beast::iequals(e.name_string(), last->name_string()))
{
BOOST_ASSERT(count(e.name_string()) == 0);
set_.insert_before(before, e);
list_.push_back(e);
return;
}
// keep duplicate fields together in the list
set_.insert_before(before, e);
list_.insert(++list_.iterator_to(*last), e);
}
template<class Allocator>

View File

@ -72,6 +72,9 @@ public:
check("beast.http", error::stale_parser);
check("beast.http", error::short_read);
check("beast.http", error::header_field_name_too_large);
check("beast.http", error::header_field_value_too_large);
}
};

View File

@ -445,6 +445,33 @@ public:
BEAST_EXPECT(std::next(rng.first, 1)->value() == "4");
BEAST_EXPECT(std::next(rng.first, 2)->value() == "6");
}
// max field name and max field value
{
fields f;
error_code ec;
auto fit_name = std::string(fields::max_name_size, 'a');
auto big_name = std::string(fields::max_name_size + 1, 'a');
auto fit_value = std::string(fields::max_value_size, 'a');
auto big_value = std::string(fields::max_value_size + 1, 'a');
f.insert(fit_name, fit_value);
f.set(fit_name, fit_value);
f.insert(field::age, big_name, "", ec);
BEAST_EXPECT(ec == error::header_field_name_too_large);
f.insert(field::age, "", big_value, ec);
BEAST_EXPECT(ec == error::header_field_value_too_large);
BEAST_THROWS(f.insert(field::age, big_value), boost::system::system_error);
BEAST_THROWS(f.insert(field::age, big_name, ""), boost::system::system_error);
BEAST_THROWS(f.insert(field::age, "", big_value), boost::system::system_error);
BEAST_THROWS(f.insert(big_name, ""), boost::system::system_error);
BEAST_THROWS(f.insert("", big_value), boost::system::system_error);
BEAST_THROWS(f.set(field::age, big_value), boost::system::system_error);
BEAST_THROWS(f.set(big_name, ""), boost::system::system_error);
BEAST_THROWS(f.set("", big_value), boost::system::system_error);
}
}
struct sized_body