From 82e28aa4f30fc70bc0cc14fb324187d640e3aeff Mon Sep 17 00:00:00 2001 From: yhirose Date: Fri, 16 Oct 2026 16:39:00 -0400 Subject: [PATCH] Hash case-insensitive keys in a loop case_ignore::hash recursed once per character of the key, and trailer field names from the wire reach it, so a long one could overflow the stack of a build that keeps the recursion (-O0). Hash in a loop instead, which gives the same values. --- httplib.h | 18 +++++++----------- test/test.cc | 9 +++++++++ 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/httplib.h b/httplib.h index facb8b79..2030a352 100644 --- a/httplib.h +++ b/httplib.h @@ -676,17 +676,13 @@ struct equal_to { struct hash { size_t operator()(const std::string &key) const { - return hash_core(key.data(), key.size(), 0); - } - - size_t hash_core(const char *s, size_t l, size_t h) const { - return (l == 0) ? h - : hash_core(s + 1, l - 1, - // Unsets the 6 high bits of h, therefore no - // overflow happens - (((std::numeric_limits::max)() >> 6) & - h * 33) ^ - static_cast(to_lower(*s))); + size_t h = 0; + for (auto c : key) { + // Unsets the 6 high bits of h, therefore no overflow happens + h = (((std::numeric_limits::max)() >> 6) & h * 33) ^ + static_cast(to_lower(c)); + } + return h; } }; diff --git a/test/test.cc b/test/test.cc index bb878cfd..35f78f59 100644 --- a/test/test.cc +++ b/test/test.cc @@ -643,6 +643,15 @@ TEST(FileExtensionTest, TakesTheAlphanumericSuffix) { EXPECT_EQ(long_ext, detail::file_extension("a." + long_ext)); } +TEST(CaseIgnoreHashTest, IgnoresCaseAndTakesLongKeys) { + detail::case_ignore::hash hash; + EXPECT_EQ(hash("Content-Type"), hash("content-TYPE")); + EXPECT_NE(hash("Content-Type"), hash("Content-Length")); + + // Long enough to overflow the stack if hashed one call per character. + EXPECT_EQ(hash(std::string(1 << 20, 'A')), hash(std::string(1 << 20, 'a'))); +} + TEST(FindContentTypeTest, ComparesWholeExtensions) { EXPECT_EQ("text/html", detail::find_content_type("/a.html", {}, "")); EXPECT_EQ("image/svg+xml", detail::find_content_type("/a.svg", {}, ""));