Skip to content

fix: cache compiled regexps in CORS wildcard origin matching - #4563

Merged
akshaydeo merged 4 commits into
maximhq:devfrom
matiasinsaurralde:fix/cache-cors-wildcard-regexp-compilation
Jun 21, 2026
Merged

fix: cache compiled regexps in CORS wildcard origin matching#4563
akshaydeo merged 4 commits into
maximhq:devfrom
matiasinsaurralde:fix/cache-cors-wildcard-regexp-compilation

Conversation

@matiasinsaurralde

Copy link
Copy Markdown
Contributor

matchesWildcardPattern was calling regexp.Compile on every HTTP request for each wildcard pattern in AllowedOrigins. Add a sync.Map cache keyed by the raw pattern string so each regexp is compiled once and reused.

Benchmarks show ~36x speedup sequential, ~350x under concurrency, with zero allocations on the hot path.

Also adds unit tests for matchesWildcardPattern and IsOriginAllowed.

Summary

matchesWildcardPattern compiled a new *regexp.Regexp on every call — which happens on every HTTP request through the CORS middleware, plus every WebSocket upgrade. Since the wildcard patterns come from static config (AllowedOrigins), the same patterns were recompiled thousands of times per second for no reason.

This adds a sync.Map cache so each pattern is compiled once on first encounter and reused thereafter. This follows the same pattern already used by celMapKeyRegexCache in plugins/governance/routing.go for CEL map-key regex caching.

Changes

  • Added wildcardRegexpCache sync.Map in handlers/utils.go with cache-first lookup in matchesWildcardPattern
  • Added "sync" import
  • Added handlers/wildcard_test.go with 26 test cases covering matchesWildcardPattern and IsOriginAllowed

Benchmark (benchstat, 8 rounds, main vs fix)

Benchmark main fix delta
MatchesWildcardPattern 11,595 ns 320 ns -97.24% (36x)
MatchesWildcardPattern_Parallel 3,501 ns 10 ns -99.71% (350x)
IsOriginAllowed_WithWildcards 15,278 ns 527 ns -96.55% (29x)
IsOriginAllowed_WithWildcards_Parallel 4,080 ns 23 ns -99.44% (178x)
IsOriginAllowed_ExactOnly 24 ns 24 ns ~ (no regression)

Memory drops to 0 B/op, 0 allocs/op across all wildcard benchmarks (from 27-36 KiB and 340-422 allocs/op on main).

Benchmark:
wildcard_bench_test.txt

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

cd transports/bifrost-http
go test ./handlers/... -run='TestMatchesWildcard|TestIsOriginAllowed' -v

@CLAassistant

CLAassistant commented Jun 19, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 61665f34-59ef-4e61-96c4-f19e99104fbf

📥 Commits

Reviewing files that changed from the base of the PR and between 372f0b0 and ff68c93.

📒 Files selected for processing (2)
  • transports/bifrost-http/handlers/utils.go
  • transports/bifrost-http/handlers/wildcard_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • transports/bifrost-http/handlers/utils.go
  • transports/bifrost-http/handlers/wildcard_test.go

📝 Walkthrough

Summary by CodeRabbit

  • Performance Improvements

    • Improved wildcard origin allowlisting by caching compiled wildcard matching rules for faster repeated evaluations.
  • Tests

    • Added unit tests covering wildcard pattern matching and allowlisting behavior, including cache consistency, scheme/wildcard handling, subdomain and slash restrictions, and edge cases (such as empty inputs and "*" semantics).

Walkthrough

matchesWildcardPattern in utils.go now maintains a package-level sync.Map cache of compiled wildcard regexes, reusing them on repeated calls and storing via LoadOrStore. A new test file adds table-driven and cache-consistency tests for matchesWildcardPattern and IsOriginAllowed.

Changes

Wildcard Regex Cache and Test Coverage

Layer / File(s) Summary
sync.Map regex cache in matchesWildcardPattern
transports/bifrost-http/handlers/utils.go
Adds sync import and a package-level sync.Map; matchesWildcardPattern now performs a cache lookup first, compiles and stores the regex on a miss via LoadOrStore, and returns false on compile error.
Table-driven tests for pattern matching and origin allowlisting
transports/bifrost-http/handlers/wildcard_test.go
Adds TestMatchesWildcardPattern (scheme handling, wildcard placement, dot/subdomain rules, slash rejection, empty inputs), TestMatchesWildcardPattern_CacheConsistency (repeated-call consistency), and TestIsOriginAllowed (localhost, exact match, global wildcard, wildcard patterns, mixed lists, empty edge cases).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 A map of regexes, tidy and neat,
Compiled but once, then cached on repeat.
No slash shall pass my wildcard gate,
Each origin tested at a tidy rate.
Sync.Map in hand, the bunny hops free! 🗺️

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately and concisely summarizes the main change: adding caching of compiled regexps in CORS wildcard origin matching.
Description check ✅ Passed The PR description covers all major template sections including Summary, Changes, Type of change, Affected areas, How to test, and includes comprehensive benchmark results demonstrating the performance improvement.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 5/5

Safe to merge — the change is a pure performance optimization with no behavioral difference; correctness is preserved by the LoadOrStore return-value pattern and the existing logic is unchanged.

The caching implementation is correct: LoadOrStore returns the value actually in the map, so concurrent first-compile races produce functionally identical results with no incorrect state. The wildcard regex translation is unchanged, and the cache key (raw pattern string) guarantees the same regexp is always used for a given pattern regardless of config reload ordering.

No files require special attention.

Important Files Changed

Filename Overview
transports/bifrost-http/handlers/utils.go Adds package-level wildcardRegexpCache sync.Map and updates matchesWildcardPattern to compile each regex once via cache-first Load then LoadOrStore; correctness and concurrency handling are sound.
transports/bifrost-http/handlers/wildcard_test.go New test file with 26 table-driven cases for matchesWildcardPattern and IsOriginAllowed, plus a cache-consistency test; one inline test comment is misleading but doesn't affect correctness.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[HTTP Request with Origin header] --> B[IsOriginAllowed]
    B --> C{isLocalhostOrigin?}
    C -- yes --> D[return true]
    C -- no --> E[iterate allowedOrigins]
    E --> F{exact match or '*'?}
    F -- yes --> D
    F -- no --> G{contains '*'?}
    G -- no --> H[next origin]
    H --> E
    G -- yes --> I[matchesWildcardPattern]
    I --> J{wildcardRegexpCache.Load hit?}
    J -- yes --> K[use cached *regexp.Regexp]
    J -- no --> L[regexp.QuoteMeta + replace \* with regex]
    L --> M[regexp.Compile]
    M --> N[wildcardRegexpCache.LoadOrStore]
    N --> O[use actual stored *regexp.Regexp]
    K --> P{MatchString origin?}
    O --> P
    P -- true --> D
    P -- false --> H
    E -- exhausted --> Q[return false]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[HTTP Request with Origin header] --> B[IsOriginAllowed]
    B --> C{isLocalhostOrigin?}
    C -- yes --> D[return true]
    C -- no --> E[iterate allowedOrigins]
    E --> F{exact match or '*'?}
    F -- yes --> D
    F -- no --> G{contains '*'?}
    G -- no --> H[next origin]
    H --> E
    G -- yes --> I[matchesWildcardPattern]
    I --> J{wildcardRegexpCache.Load hit?}
    J -- yes --> K[use cached *regexp.Regexp]
    J -- no --> L[regexp.QuoteMeta + replace \* with regex]
    L --> M[regexp.Compile]
    M --> N[wildcardRegexpCache.LoadOrStore]
    N --> O[use actual stored *regexp.Regexp]
    K --> P{MatchString origin?}
    O --> P
    P -- true --> D
    P -- false --> H
    E -- exhausted --> Q[return false]
Loading

Reviews (5): Last reviewed commit: "Merge branch 'dev' into fix/cache-cors-w..." | Re-trigger Greptile

Comment thread transports/bifrost-http/handlers/utils.go Outdated
Comment thread transports/bifrost-http/handlers/wildcard_test.go Outdated
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 19, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 19, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 19, 2026
@matiasinsaurralde
matiasinsaurralde force-pushed the fix/cache-cors-wildcard-regexp-compilation branch from 87ddba5 to 1964953 Compare June 19, 2026 17:52
@matiasinsaurralde
matiasinsaurralde requested a review from a team as a code owner June 19, 2026 17:52
@matiasinsaurralde
matiasinsaurralde force-pushed the fix/cache-cors-wildcard-regexp-compilation branch from 1964953 to 21e7839 Compare June 19, 2026 17:55
@akshaydeo

Copy link
Copy Markdown
Contributor

@matiasinsaurralde ❤️ for a kickass PR - could you please rebase it with dev?

@matiasinsaurralde
matiasinsaurralde force-pushed the fix/cache-cors-wildcard-regexp-compilation branch 2 times, most recently from e48fafa to c378dae Compare June 19, 2026 18:02
@matiasinsaurralde

Copy link
Copy Markdown
Contributor Author

@matiasinsaurralde ❤️ for a kickass PR - could you please rebase it with dev?

Just rebased, should be good now

@matiasinsaurralde
matiasinsaurralde force-pushed the fix/cache-cors-wildcard-regexp-compilation branch from c378dae to 372f0b0 Compare June 19, 2026 18:05
matchesWildcardPattern was calling regexp.Compile on every HTTP request
for each wildcard pattern in AllowedOrigins. Add a sync.Map cache keyed
by the raw pattern string so each regexp is compiled once and reused.

Benchmarks show ~36x speedup sequential, ~350x under concurrency, with
zero allocations on the hot path.

Also adds unit tests for matchesWildcardPattern and IsOriginAllowed.

Signed-off-by: Matías Insaurralde <matias@insaurral.de>
Use the actual stored value from LoadOrStore rather than the locally
compiled regexp, making the concurrent-safety intent explicit without
requiring readers to reason about functional equivalence of duplicate
compiles.

Signed-off-by: Matías Insaurralde <matias@insaurral.de>
Rename "scheme-less match" (want: false) to "scheme-less no match with
scheme prefix" so the test name reflects the expected outcome.

Signed-off-by: Matías Insaurralde <matias@insaurral.de>
@matiasinsaurralde
matiasinsaurralde force-pushed the fix/cache-cors-wildcard-regexp-compilation branch from 372f0b0 to ff68c93 Compare June 20, 2026 00:06
@akshaydeo
akshaydeo merged commit b894c09 into maximhq:dev Jun 21, 2026
5 of 6 checks passed
akshaydeo added a commit that referenced this pull request Jun 21, 2026
* fix: cache compiled regexps in CORS wildcard origin matching

matchesWildcardPattern was calling regexp.Compile on every HTTP request
for each wildcard pattern in AllowedOrigins. Add a sync.Map cache keyed
by the raw pattern string so each regexp is compiled once and reused.

Benchmarks show ~36x speedup sequential, ~350x under concurrency, with
zero allocations on the hot path.

Also adds unit tests for matchesWildcardPattern and IsOriginAllowed.

Signed-off-by: Matías Insaurralde <matias@insaurral.de>

* fix: use LoadOrStore return value in wildcard regexp cache

Use the actual stored value from LoadOrStore rather than the locally
compiled regexp, making the concurrent-safety intent explicit without
requiring readers to reason about functional equivalence of duplicate
compiles.

Signed-off-by: Matías Insaurralde <matias@insaurral.de>

* fix: rename misleading test case for scheme-less wildcard pattern

Rename "scheme-less match" (want: false) to "scheme-less no match with
scheme prefix" so the test name reflects the expected outcome.

Signed-off-by: Matías Insaurralde <matias@insaurral.de>

---------

Signed-off-by: Matías Insaurralde <matias@insaurral.de>
Co-authored-by: Akshay Deo <akshay@akshaydeo.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants