Stop at the last match when getting a value by index

get_header_value(), get_header_value_u64(), get_multimap_value() and
MultipartFormData::get_field() advanced an equal_range() iterator by
the caller's index after casting it to ssize_t. An index that is
negative as a signed value stepped back onto an entry of another key,
and a huge positive one kept looping after the last match.

Advance with bounded_next(), which stops at the end of the range.
This commit is contained in:
yhirose committed 2026-10-12 22:59:55 -04:00
1 parent cbaf751da1
commit 11daa99c86
2 files changed
+35 -10

No files matched your search

+13 -10
View File
@@ -1141,8 +1141,7 @@ private:
pointer operator->() const { return data_ + idx_; }
iterator_t &operator++() {
// Saturating, so that advancing past the last entry of a key (which
// get_multimap_value() does when asked for an out-of-range id) stays at
// Saturating, so that advancing past the last entry of a key stays at
// end() instead of running off the container.
if (idx_ >= size_) { return *this; }
++idx_;
@@ -3477,13 +3476,20 @@ inline bool is_numeric(const std::string &str) {
return !str.empty() && std::all_of(str.cbegin(), str.cend(), is_ascii_digit);
}
// std::ranges::next(first, n, last) for C++11
template <typename It> inline It bounded_next(It first, size_t n, It last) {
for (; n > 0 && first != last; --n) {
++first;
}
return first;
}
inline size_t get_header_value_u64(const Headers &headers,
const std::string &key, size_t def,
size_t id, bool &is_invalid_value) {
is_invalid_value = false;
auto rng = headers.equal_range(key);
auto it = rng.first;
std::advance(it, static_cast<ssize_t>(id));
auto it = bounded_next(rng.first, id, rng.second);
if (it != rng.second) {
if (is_numeric(it->second)) {
// Parse at size_t width so an out-of-range Content-Length is reported
@@ -8146,8 +8152,7 @@ inline const char *get_header_value(const Headers &headers,
}
auto rng = headers.equal_range(key);
auto it = rng.first;
std::advance(it, static_cast<ssize_t>(id));
auto it = bounded_next(rng.first, id, rng.second);
if (it != rng.second) { return it->second.c_str(); }
return def;
}
@@ -8200,8 +8205,7 @@ template <typename Map>
inline typename Map::mapped_type
get_multimap_value(const Map &m, const std::string &key, size_t id) {
auto rng = m.equal_range(key);
auto it = rng.first;
std::advance(it, static_cast<ssize_t>(id));
auto it = bounded_next(rng.first, id, rng.second);
if (it != rng.second) { return it->second; }
return typename Map::mapped_type();
}
@@ -11728,8 +11732,7 @@ inline bool Request::is_multipart_form_data() const {
inline std::string MultipartFormData::get_field(const std::string &key,
size_t id) const {
auto rng = fields.equal_range(key);
auto it = rng.first;
std::advance(it, static_cast<ssize_t>(id));
auto it = detail::bounded_next(rng.first, id, rng.second);
if (it != rng.second) { return it->second.content; }
return std::string();
}