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 unexpected end-of-file condition is encountered while trying
to read from the file. 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 } // http

View File

@ -10,9 +10,10 @@
#ifndef BOOST_BEAST_HTTP_FIELDS_HPP #ifndef BOOST_BEAST_HTTP_FIELDS_HPP
#define 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/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/beast/http/field.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <boost/core/empty_value.hpp> #include <boost/core/empty_value.hpp>
@ -218,6 +219,14 @@ private:
public: 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 /// Destructor
~basic_fields(); ~basic_fields();
@ -434,13 +443,15 @@ public:
@param name The field name. @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 void
insert(field name, string_view const& value); insert(field name, string_view value);
/* Set a field from a null pointer (deleted).
*/
void void
insert(field, std::nullptr_t) = delete; 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 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 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 void
insert(string_view, std::nullptr_t) = delete; insert(string_view, std::nullptr_t) = delete;
@ -477,15 +492,50 @@ public:
must be equal to `to_string(name)` using a case-insensitive must be equal to `to_string(name)` using a case-insensitive
comparison, otherwise the behavior is undefined. 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 void
insert(field name, string_view name_string, insert(field name, string_view name_string,
string_view const& value); string_view value);
void void
insert(field, string_view, std::nullptr_t) = delete; 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. /** Set a field value, removing any other instances of that field.
First removes any values with matching field names, then First removes any values with matching field names, then
@ -493,13 +543,14 @@ public:
@param name The field name. @param name The field name.
@param value The value of the field, as a @ref boost::beast::string_view @param value The field value.
@return 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 void
set(field name, string_view const& value); set(field name, string_view value);
void void
set(field, std::nullptr_t) = delete; set(field, std::nullptr_t) = delete;
@ -511,15 +562,21 @@ public:
@param name The field name. It is interpreted as a case-insensitive string. @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 void
set(string_view name, string_view const& value); set(string_view name, string_view value);
void void
set(string_view, std::nullptr_t) = delete; set(string_view, std::nullptr_t) = delete;
/** Remove a field. /** Remove a field.
References and iterators to the erased elements are References and iterators to the erased elements are
invalidated. Other references and iterators are not invalidated. Other references and iterators are not
@ -744,9 +801,21 @@ private:
template<class OtherAlloc> template<class OtherAlloc>
friend class basic_fields; friend class basic_fields;
element*
try_create_new_element(
field name,
string_view sname,
string_view value,
error_code& ec);
element& element&
new_element(field name, new_element(
string_view sname, string_view value); field name,
string_view sname,
string_view value);
void
insert_element(element& e);
void void
delete_element(element& e); delete_element(element& e);

View File

@ -61,6 +61,8 @@ public:
case error::multiple_content_length: return "multiple Content-Length"; case error::multiple_content_length: return "multiple Content-Length";
case error::stale_parser: return "stale parser"; case error::stale_parser: return "stale parser";
case error::short_read: return "unexpected eof in body"; 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: default:
return "beast.http error"; return "beast.http error";

View File

@ -537,7 +537,7 @@ template<class Allocator>
inline inline
void void
basic_fields<Allocator>:: basic_fields<Allocator>::
insert(field name, string_view const& value) insert(field name, string_view value)
{ {
BOOST_ASSERT(name != field::unknown); BOOST_ASSERT(name != field::unknown);
insert(name, to_string(name), value); insert(name, to_string(name), value);
@ -546,58 +546,52 @@ insert(field name, string_view const& value)
template<class Allocator> template<class Allocator>
void void
basic_fields<Allocator>:: basic_fields<Allocator>::
insert(string_view sname, string_view const& value) insert(string_view sname, string_view value)
{ {
auto const name = insert(
string_to_field(sname); string_to_field(sname), sname, value);
insert(name, 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> template<class Allocator>
void void
basic_fields<Allocator>:: basic_fields<Allocator>::
insert(field name, insert(field name,
string_view sname, string_view const& value) string_view sname, string_view value)
{ {
auto& e = new_element(name, sname, insert_element(
static_cast<string_view>(value)); new_element(name, sname, 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);
} }
template<class Allocator> template<class Allocator>
void void
basic_fields<Allocator>:: basic_fields<Allocator>::
set(field name, string_view const& value) set(field name, string_view value)
{ {
BOOST_ASSERT(name != field::unknown); BOOST_ASSERT(name != field::unknown);
set_element(new_element(name, to_string(name), set_element(
static_cast<string_view>(value))); new_element(name, to_string(name), value));
} }
template<class Allocator> template<class Allocator>
void void
basic_fields<Allocator>:: basic_fields<Allocator>::
set(string_view sname, string_view const& value) set(string_view sname, string_view value)
{ {
set_element(new_element( set_element(new_element(
string_to_field(sname), sname, value)); string_to_field(sname), sname, value));
@ -953,18 +947,22 @@ set_keep_alive_impl(
template<class Allocator> template<class Allocator>
auto auto
basic_fields<Allocator>:: basic_fields<Allocator>::
new_element(field name, try_create_new_element(
string_view sname, string_view value) -> field name,
element& string_view sname,
string_view value,
error_code& ec) -> element*
{ {
if(sname.size() + 2 > if(sname.size() > max_name_size)
(std::numeric_limits<off_t>::max)()) {
BOOST_THROW_EXCEPTION(std::length_error{ BOOST_BEAST_ASSIGN_EC(ec, error::header_field_name_too_large);
"field name too large"}); return nullptr;
if(value.size() + 2 > }
(std::numeric_limits<off_t>::max)()) if(value.size() > max_value_size)
BOOST_THROW_EXCEPTION(std::length_error{ {
"field value too large"}); BOOST_BEAST_ASSIGN_EC(ec, error::header_field_value_too_large);
return nullptr;
}
value = detail::trim(value); value = detail::trim(value);
std::uint16_t const off = std::uint16_t const off =
static_cast<off_t>(sname.size() + 2); static_cast<off_t>(sname.size() + 2);
@ -974,7 +972,50 @@ new_element(field name,
auto const p = alloc_traits::allocate(a, auto const p = alloc_traits::allocate(a,
(sizeof(element) + off + len + 2 + sizeof(align_type) - 1) / (sizeof(element) + off + len + 2 + sizeof(align_type) - 1) /
sizeof(align_type)); 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> template<class Allocator>

View File

@ -72,6 +72,9 @@ public:
check("beast.http", error::stale_parser); check("beast.http", error::stale_parser);
check("beast.http", error::short_read); 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, 1)->value() == "4");
BEAST_EXPECT(std::next(rng.first, 2)->value() == "6"); 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 struct sized_body