From e35f0451872a04ce542979dda7122c7efcb1b075 Mon Sep 17 00:00:00 2001 From: Lev Date: Tue, 14 Apr 2026 03:01:55 +0200 Subject: [PATCH 1/2] fix(descriptor): use _normalize_header_name in header fast-path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two-step fast-path for header lookup applied only one of the two transformations OpenResty performs (lowercase + hyphen→underscore): name_underscore = gsub(name, "-", "_") -- preserves case → X_E2E_Key name_lower = lower(name) -- preserves hyphens → x-e2e-key For any limit_key using an uppercase+hyphenated name (e.g. X-E2E-Key) with an OpenResty-normalized header table (x_e2e_key), both lookups miss. Every such request fell through to the O(n) pairs() scan. Fix: replace the two broken lookups with a single call to the already- defined _normalize_header_name helper, which applies both transformations together. The O(n) fallback is retained as a last-resort but should no longer be reached for normal hyphenated header names. AC-7c (new scenario) exercises the uppercase+hyphenated case that the old fast-path could not handle without the fallback scan. --- src/fairvisor/descriptor.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fairvisor/descriptor.lua b/src/fairvisor/descriptor.lua index 38ca43c..f99019f 100644 --- a/src/fairvisor/descriptor.lua +++ b/src/fairvisor/descriptor.lua @@ -249,9 +249,9 @@ function _M.extract(limit_keys, request_context) value = headers[name] if value == nil then -- OpenResty normalizes header names: lowercase and hyphen to underscore (e.g. X-E2E-Key -> x_e2e_key). - local name_underscore = string_gsub(name, "-", "_") - local name_lower = string_lower(name) - value = headers[name_underscore] or headers[name_lower] + -- Use _normalize_header_name so both transformations are applied together; the two-step + -- approach (hyphen→underscore OR lowercase separately) misses uppercase+hyphenated keys. + value = headers[_normalize_header_name(name)] end if value == nil then local name_norm = _normalize_header_name(name) From 7cb9ec7a673dead82f3120e834b6e08a6493ef05 Mon Sep 17 00:00:00 2001 From: Lev Date: Tue, 14 Apr 2026 03:02:07 +0200 Subject: [PATCH 2/2] test(descriptor): add AC-7c for uppercase+hyphenated header key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AC-7b already tests lowercase hyphenated name (x-e2e-key → x_e2e_key). AC-7c exercises the case the old two-step fast-path silently missed: an uppercase+hyphenated limit_key (X-E2E-Key) where OpenResty has normalized the header to x_e2e_key. The old code would have fallen through to the O(n) pairs() scan; the fixed code resolves it in O(1). --- spec/unit/features/descriptor.feature | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/unit/features/descriptor.feature b/spec/unit/features/descriptor.feature index c226a86..e2904dc 100644 --- a/spec/unit/features/descriptor.feature +++ b/spec/unit/features/descriptor.feature @@ -44,6 +44,10 @@ Feature: Descriptor extraction and claim resolution Scenario: AC-7b header limit_key with hyphens matches OpenResty normalized key with underscores Given limit_keys is "header:x-e2e-key" And header "x_e2e_key" is "exhaust-abc123" + + Scenario: AC-7c uppercase header limit_key matches OpenResty normalized key (lowercase + underscore) + Given limit_keys is "header:X-E2E-Key" + And header "x_e2e_key" is "exhaust-abc123" When I extract descriptors Then descriptor "header:x-e2e-key" is "exhaust-abc123" And missing keys are empty