mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-21 11:39:48 +00:00
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:
1 parent
cbaf751da1
commit
11daa99c86
2 files changed
+35
-10
No files matched your search
@@ -1141,8 +1141,7 @@ private:
|
|||||||
pointer operator->() const { return data_ + idx_; }
|
pointer operator->() const { return data_ + idx_; }
|
||||||
|
|
||||||
iterator_t &operator++() {
|
iterator_t &operator++() {
|
||||||
// Saturating, so that advancing past the last entry of a key (which
|
// Saturating, so that advancing past the last entry of a key stays at
|
||||||
// get_multimap_value() does when asked for an out-of-range id) stays at
|
|
||||||
// end() instead of running off the container.
|
// end() instead of running off the container.
|
||||||
if (idx_ >= size_) { return *this; }
|
if (idx_ >= size_) { return *this; }
|
||||||
++idx_;
|
++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);
|
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,
|
inline size_t get_header_value_u64(const Headers &headers,
|
||||||
const std::string &key, size_t def,
|
const std::string &key, size_t def,
|
||||||
size_t id, bool &is_invalid_value) {
|
size_t id, bool &is_invalid_value) {
|
||||||
is_invalid_value = false;
|
is_invalid_value = false;
|
||||||
auto rng = headers.equal_range(key);
|
auto rng = headers.equal_range(key);
|
||||||
auto it = rng.first;
|
auto it = bounded_next(rng.first, id, rng.second);
|
||||||
std::advance(it, static_cast<ssize_t>(id));
|
|
||||||
if (it != rng.second) {
|
if (it != rng.second) {
|
||||||
if (is_numeric(it->second)) {
|
if (is_numeric(it->second)) {
|
||||||
// Parse at size_t width so an out-of-range Content-Length is reported
|
// 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 rng = headers.equal_range(key);
|
||||||
auto it = rng.first;
|
auto it = bounded_next(rng.first, id, rng.second);
|
||||||
std::advance(it, static_cast<ssize_t>(id));
|
|
||||||
if (it != rng.second) { return it->second.c_str(); }
|
if (it != rng.second) { return it->second.c_str(); }
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
@@ -8200,8 +8205,7 @@ template <typename Map>
|
|||||||
inline typename Map::mapped_type
|
inline typename Map::mapped_type
|
||||||
get_multimap_value(const Map &m, const std::string &key, size_t id) {
|
get_multimap_value(const Map &m, const std::string &key, size_t id) {
|
||||||
auto rng = m.equal_range(key);
|
auto rng = m.equal_range(key);
|
||||||
auto it = rng.first;
|
auto it = bounded_next(rng.first, id, rng.second);
|
||||||
std::advance(it, static_cast<ssize_t>(id));
|
|
||||||
if (it != rng.second) { return it->second; }
|
if (it != rng.second) { return it->second; }
|
||||||
return typename Map::mapped_type();
|
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,
|
inline std::string MultipartFormData::get_field(const std::string &key,
|
||||||
size_t id) const {
|
size_t id) const {
|
||||||
auto rng = fields.equal_range(key);
|
auto rng = fields.equal_range(key);
|
||||||
auto it = rng.first;
|
auto it = detail::bounded_next(rng.first, id, rng.second);
|
||||||
std::advance(it, static_cast<ssize_t>(id));
|
|
||||||
if (it != rng.second) { return it->second.content; }
|
if (it != rng.second) { return it->second.content; }
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1923,6 +1923,28 @@ TEST(HeadersOrderTest, IdBeyondTheLastDuplicateYieldsDefault) {
|
|||||||
EXPECT_EQ("def", req.get_header_value("X-Missing", "def", 3));
|
EXPECT_EQ("def", req.get_header_value("X-Missing", "def", 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(HeadersOrderTest, HugeIdYieldsDefault) {
|
||||||
|
Request req;
|
||||||
|
req.set_header("X-First", "1");
|
||||||
|
req.set_header("X-Test", "2");
|
||||||
|
req.params.emplace("first", "1");
|
||||||
|
req.params.emplace("test", "2");
|
||||||
|
req.form.fields.emplace("first", FormField{"first", "1", {}});
|
||||||
|
req.form.fields.emplace("test", FormField{"test", "2", {}});
|
||||||
|
req.form.files.emplace("first", FormData{"first", "1", "", "", {}});
|
||||||
|
req.form.files.emplace("test", FormData{"test", "2", "", "", {}});
|
||||||
|
|
||||||
|
// The second one is negative when read as a signed value
|
||||||
|
const auto max_id = (std::numeric_limits<size_t>::max)();
|
||||||
|
for (auto id : {max_id / 2, max_id}) {
|
||||||
|
EXPECT_EQ("def", req.get_header_value("X-Test", "def", id));
|
||||||
|
EXPECT_EQ(7U, req.get_header_value_u64("X-Test", 7, id));
|
||||||
|
EXPECT_EQ("", req.get_param_value("test", id));
|
||||||
|
EXPECT_EQ("", req.form.get_field("test", id));
|
||||||
|
EXPECT_EQ("", req.form.get_file("test", id).content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST(HeadersOrderTest, TraversalFollowsInsertionOrder) {
|
TEST(HeadersOrderTest, TraversalFollowsInsertionOrder) {
|
||||||
Headers headers;
|
Headers headers;
|
||||||
headers.emplace("Host", "example.com");
|
headers.emplace("Host", "example.com");
|
||||||
|
|||||||
Reference in new issue
Block a user