Skip to content
This repository was archived by the owner on Apr 24, 2026. It is now read-only.

fix(descriptor): use _normalize_header_name in header fast-path#154

Open
levleontiev wants to merge 3 commits into
mainfrom
fix/descriptor-header-normalization-fast-path
Open

fix(descriptor): use _normalize_header_name in header fast-path#154
levleontiev wants to merge 3 commits into
mainfrom
fix/descriptor-header-normalization-fast-path

Conversation

@levleontiev
Copy link
Copy Markdown
Contributor

Summary

The two-step fast-path for header lookup in descriptor.lua was applying only one transformation at a time, while OpenResty applies both simultaneously (lowercase + hyphen→underscore):

-- BEFORE (broken for uppercase+hyphenated names):
local name_underscore = string_gsub(name, "-", "_")  -- e.g. X-E2E-Key → X_E2E_Key (wrong: case preserved)
local name_lower      = string_lower(name)            -- e.g. X-E2E-Key → x-e2e-key (wrong: hyphens preserved)
value = headers[name_underscore] or headers[name_lower]
-- Both miss when headers dict is OpenResty-normalized: {"x_e2e_key": "val"}
-- Falls through to O(n) pairs() scan every time

Fix

Replace with a single call to the already-defined _normalize_header_name helper that applies both transformations:

-- AFTER (correct):
value = headers[_normalize_header_name(name)]
-- _normalize_header_name("X-E2E-Key") = "x_e2e_key" → O(1) hit

Impact

  • Any limit key using an uppercase+hyphenated header name (e.g. header:X-E2E-Key, header:X-API-Key) with an OpenResty-normalized header table was doing an O(n) pairs() scan on every request instead of an O(1) lookup.
  • Correctness was maintained (fallback scan found the value), but performance degraded linearly with response header count.

Why tests didn't catch this

AC-7b uses a lowercase limit key (header:x-e2e-key), so string_gsub("x-e2e-key", "-", "_") = "x_e2e_key" — the fast-path happened to work. AC-7c (new) covers the uppercase case.

🤖 Generated with Claude Code

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.
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).
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant