Skip to content

🐛 fix(cache): detect directives with OWS and value params#4212

Closed
nickzerjeski wants to merge 1 commit intogofiber:mainfrom
nickzerjeski:fix-cache-hasdirective-4143
Closed

🐛 fix(cache): detect directives with OWS and value params#4212
nickzerjeski wants to merge 1 commit intogofiber:mainfrom
nickzerjeski:fix-cache-hasdirective-4143

Conversation

@nickzerjeski
Copy link
Copy Markdown

@nickzerjeski nickzerjeski commented Apr 13, 2026

Description

Fixes directive matching in cache middleware so valid Cache-Control / Pragma directives are recognized when followed by optional whitespace or by = parameters.

Fixes #4143

Changes introduced

  • Replaced manual substring scanning in hasDirective with directive-key parsing via parseCacheControlDirectives.

  • Added regression tests for directive terminators and edge cases:

    • trailing space (no-cache )
    • trailing tab (no-cache\t)
    • parameterized form (no-cache="Set-Cookie")
    • negative matches (my-no-cache, private-ish)
  • Benchmarks: Not applicable (logic correctness fix).

  • Documentation Update: Not required.

  • Changelog/What's New: Not required.

  • Migration Guide: Not required.

  • API Alignment with Express: Not applicable.

  • API Longevity: Uses existing directive parser path for more robust token matching.

  • Examples: Added table-driven examples in tests.

Type of change

  • New feature (non-breaking change which adds functionality)
  • Enhancement (improvement to existing features and functionality)
  • Documentation update (changes to documentation)
  • Performance improvement (non-breaking change which improves efficiency)
  • Code consistency (non-breaking change which improves code reliability and robustness)

Checklist

  • Followed the inspiration of the Express.js framework for new functionalities, making them similar in usage.
  • Conducted a self-review of the code and provided comments for complex or critical parts.
  • Updated the documentation in the /docs/ directory for Fiber's documentation.
  • Added or updated unit tests to validate the effectiveness of the changes or new features.
  • Ensured that new and existing unit tests pass locally with the changes. (ran targeted cache middleware tests)
  • Verified that any new dependencies are essential and have been agreed upon by the maintainers/community. (no new dependencies)
  • Aimed for optimal performance with minimal allocations in the new code.
  • Provided benchmarks for the new code to analyze and improve upon.

Commit formatting

Commit uses emoji prefix per contribution guidance.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 13, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 34a9c183-aae9-42fc-a387-6d5fd1e3d6a7

📥 Commits

Reviewing files that changed from the base of the PR and between 5c17b55 and 01b6574.

📒 Files selected for processing (2)
  • middleware/cache/cache.go
  • middleware/cache/cache_test.go

Walkthrough

Fixes Cache-Control directive detection in cache middleware by parsing comma-separated directives, trimming spaces/tabs, handling =-delimited values, and performing case-insensitive key comparisons so directives like no-cache, private, or no-cache="Set-Cookie" are correctly recognized.

Changes

Cohort / File(s) Summary
Cache Directive Detection
middleware/cache/cache.go
Replaced fragile substring/boundary checks in hasDirective with tokenized parsing: splits on commas, trims spaces/tabs, extracts directive key up to =, and does case-insensitive key comparison.
Test Coverage
middleware/cache/cache_test.go
Added Test_HasDirective with parallel subtests validating case-insensitivity, trailing space/tab handling, quoted directive values, and avoiding partial/substring matches.

Estimated Code Review Effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly Related PRs

Suggested Labels

📜 RFC Compliance

Suggested Reviewers

  • gaby
  • sixcolors
  • efectn
  • ReneWerner87

Poem

🐰 I nibble tokens, trim each edge,
No trailing space can break my pledge.
I hop through headers, case be gone,
Now directives are brightly drawn. 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.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 title clearly describes the fix: detecting directives with optional whitespace and value parameters in cache middleware.
Description check ✅ Passed The description comprehensively covers the issue, changes made, reasoning, and appropriately marks non-applicable checklist items.
Linked Issues check ✅ Passed The PR fully addresses issue #4143 by replacing substring scanning with directive parsing to recognize directives followed by whitespace, tabs, or parameter values.
Out of Scope Changes check ✅ Passed All changes focus on fixing the hasDirective function and adding comprehensive test coverage; no extraneous modifications detected.

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

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

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.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the hasDirective function in the cache middleware to use the parseCacheControlDirectives parser and adds unit tests. The review recommends using utils.UnsafeBytes to eliminate unnecessary allocations and notes that the parser needs to be updated to support horizontal tabs to comply with RFC 9111.

if i+dirLen == ccLen || cc[i+dirLen] == ',' {
return true
found := false
parseCacheControlDirectives([]byte(cc), func(key, _ []byte) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The conversion []byte(cc) creates a copy of the string, leading to an unnecessary allocation. Since parseCacheControlDirectives only performs read operations on the slice, you can use utils.UnsafeBytes(cc) for a zero-allocation conversion. This is more efficient and consistent with other parts of the middleware (e.g., line 1129).

Suggested change
parseCacheControlDirectives([]byte(cc), func(key, _ []byte) {
parseCacheControlDirectives(utils.UnsafeBytes(cc), func(key, _ []byte) {

{header: "no-cache", directive: "no-cache", expect: true},
{header: "NO-CACHE", directive: "no-cache", expect: true},
{header: "no-cache ", directive: "no-cache", expect: true},
{header: "no-cache\t", directive: "no-cache", expect: true},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This test case for a trailing horizontal tab (\t) might fail with the current implementation of parseCacheControlDirectives. The parser (lines 952, 957, and 970 in cache.go) only trims the space character (' ') and does not account for tabs. Per RFC 9111, Optional White Space (OWS) consists of both spaces and horizontal tabs. To fully support OWS as intended by this PR, the underlying parser should be updated to handle both characters.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@middleware/cache/cache.go`:
- Around line 911-913: hasDirective fails for HTAB-terminated tokens because
parseCacheControlDirectives only trims space characters; update the parser used
by hasDirective (parseCacheControlDirectives) to trim horizontal tab characters
as well (include '\t' in the trimming/whitespace set) so that values like
"no-cache\t" match directive "no-cache"; locate parseCacheControlDirectives and
modify its trimming logic (or the utilities it calls) to treat '\t' the same as
' ' so hasDirective correctly sees HTAB-terminated directives.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 033e35ad-8a75-4553-b618-d3dcf60312ee

📥 Commits

Reviewing files that changed from the base of the PR and between 4acf465 and 5c17b55.

📒 Files selected for processing (2)
  • middleware/cache/cache.go
  • middleware/cache/cache_test.go

@nickzerjeski nickzerjeski force-pushed the fix-cache-hasdirective-4143 branch from 5c17b55 to 01b6574 Compare April 13, 2026 08:26
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes directive matching in cache middleware so Cache-Control / Pragma directives are recognized when followed by optional whitespace or by = parameters (e.g., no-cache , no-cache="Set-Cookie").

Changes:

  • Refactors hasDirective to use parseCacheControlDirectives for directive-key matching.
  • Adds regression tests covering directive terminators, parameterized directives, and negative matches.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
middleware/cache/cache.go Switches hasDirective from substring scanning to directive parsing for more robust matching.
middleware/cache/cache_test.go Adds table-driven tests to prevent regressions for directive terminators and edge cases.

Comment on lines +910 to 915
start := 0
for start < len(cc) {
end := start
for end < len(cc) && cc[end] != ',' {
end++
}
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parseCacheControlDirectives currently only treats ' ' as optional whitespace when trimming keys/parts. As a result, for inputs like "no-cache\t" the parsed key becomes "no-cache\t" and this comparison never matches, so the bug for tab-terminated directives persists (and the new regression test will fail). Update the directive parser (or this call site) to treat \t as OWS everywhere it trims/skips whitespace (leading separators, key/value trimming).

Copilot uses AI. Check for mistakes.
if !utils.EqualFold(cc[i:i+dirLen], directive) {
continue
start := 0
for start < len(cc) {
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parseCacheControlDirectives([]byte(cc), ...) allocates a new byte slice on every call. Since the parser is read-only, prefer utils.UnsafeBytes(cc) (as used elsewhere in this file, e.g. parseMaxAge at cache.go:1086) to avoid the extra allocation in this hot path.

Suggested change
for start < len(cc) {
parseCacheControlDirectives(utils.UnsafeBytes(cc), func(key, _ []byte) {

Copilot uses AI. Check for mistakes.
Comment on lines +3068 to +3074
for _, tt := range tests {
tt := tt
t.Run(tt.header+"_"+tt.directive, func(t *testing.T) {
t.Parallel()
got := hasDirective(tt.header, tt.directive)
require.Equal(t, tt.expect, got)
})
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Subtest names are built from the raw header value (tt.header+"_"+tt.directive). Because some cases include control characters (e.g. \t) and quotes, the test output and -run filtering can become hard to read/use. Consider using a sanitized name (e.g. fmt.Sprintf("%q_%s", tt.header, tt.directive) or an explicit name field).

Copilot uses AI. Check for mistakes.
@nickzerjeski
Copy link
Copy Markdown
Author

Validation run:\n\ngo test ./middleware/cache -run 'Test_HasDirective|Test_Cache_WithNoCacheRequestDirective|Test_Cache_WithETagAndNoCacheRequestDirective|Test_AllowsSharedCache'\n\nResult: pass

@ReneWerner87
Copy link
Copy Markdown
Member

we already have a pr for this #4211

@github-project-automation github-project-automation bot moved this to Done in v3 Apr 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

🐛 [Bug]: Cache middleware hasDirective misses directives followed by space, tab, or '='

3 participants