Skip to content

fix(cache): improve hasDirective function to handle various cases and…#4181

Closed
KambojRajan wants to merge 1 commit intogofiber:mainfrom
KambojRajan:rk/fix/find-directive-in-cache-header
Closed

fix(cache): improve hasDirective function to handle various cases and…#4181
KambojRajan wants to merge 1 commit intogofiber:mainfrom
KambojRajan:rk/fix/find-directive-in-cache-header

Conversation

@KambojRajan
Copy link
Copy Markdown

… add tests

Description

Please provide a clear and concise description of the changes you've made and the problem they address. Include the purpose of the change, any relevant issues it solves, and the benefits it brings to the project. If this change introduces new features or adjustments, highlight them here.

Fixes # (issue)

Changes introduced

List the new features or adjustments introduced in this pull request. Provide details on benchmarks, documentation updates, changelog entries, and if applicable, the migration guide.

  • Benchmarks: Describe any performance benchmarks and improvements related to the changes.
  • Documentation Update: Detail the updates made to the documentation and links to the changed files.
  • Changelog/What's New: Include a summary of the additions for the upcoming release notes.
  • Migration Guide: If necessary, provide a guide or steps for users to migrate their existing code to accommodate these changes.
  • API Alignment with Express: Explain how the changes align with the Express API.
  • API Longevity: Discuss the steps taken to ensure that the new or updated APIs are consistent and not prone to breaking changes.
  • Examples: Provide examples demonstrating the new features or changes in action.

Type of change

Please delete options that are not relevant.

  • 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

Before you submit your pull request, please make sure you meet these requirements:

  • 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.
  • Verified that any new dependencies are essential and have been agreed upon by the maintainers/community.
  • Aimed for optimal performance with minimal allocations in the new code.
  • Provided benchmarks for the new code to analyze and improve upon.

Commit formatting

Please use emojis in commit messages for an easy way to identify the purpose or intention of a commit. Check out the emoji cheatsheet here: CONTRIBUTING.md

@welcome
Copy link
Copy Markdown

welcome bot commented Apr 4, 2026

Thanks for opening this pull request! 🎉 Please check out our contributing guidelines. If you need help or want to chat with us, join us on Discord https://gofiber.io/discord

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 4, 2026

Walkthrough

The hasDirective helper function in the cache middleware was refactored to correctly parse Cache-Control headers by splitting on commas, trimming whitespace, extracting directive keys before = symbols, and performing case-insensitive matching. A comprehensive test suite was added to validate the directive detection logic across multiple scenarios.

Changes

Cohort / File(s) Summary
Cache directive detection logic
middleware/cache/cache.go
Rewrote hasDirective function to split Cache-Control header on commas, trim whitespace per segment, extract directive keys before = values, and perform case-insensitive matching for correct directive detection.
Test coverage for directive detection
middleware/cache/cache_test.go
Added Test_Cache_hasDirective with 105 lines covering edge cases: comma/space boundaries, trailing whitespace, directives with values, mid-string matches, partial match rejection, empty input, and case-insensitivity.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • Issue #4143: Directly addresses the bug where hasDirective incorrectly handled directives followed by whitespace, tabs, or = symbols by implementing proper comma and whitespace-aware token parsing.

Possibly related PRs

  • PR #3534: Both PRs modify the Cache-Control directive-detection logic to improve accuracy and perform case-insensitive token matching on the same function.

Suggested labels

☢️ Bug, codex

Suggested reviewers

  • sixcolors
  • efectn
  • ReneWerner87

Poem

🐰 A cache fix hops along,
Commas split, spaces trimmed strong,
No more directive despair,
Headers parsed with utmost care, 🎉
Tests ensure none go astray!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is largely a template with placeholder text and unchecked checkboxes. While it follows the template structure, it lacks substantive information about the actual changes, problem being solved, or decision rationale. Replace placeholder text with concrete details about why hasDirective was improved, what issues it solves, what specific cases are now handled, and complete relevant checklist items with actual information.
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: improving the hasDirective function and adding tests. It is concise and specific enough for teammates to understand the primary focus.

✏️ 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.

@ReneWerner87 ReneWerner87 added this to v3 Apr 4, 2026
@ReneWerner87 ReneWerner87 added this to the v3 milestone Apr 4, 2026
@gaby
Copy link
Copy Markdown
Member

gaby commented Apr 4, 2026

@KambojRajan What exactly is the improvement?

The addition of several functions from the "strings" package may negatively impact overall performance compared to our existing utils.

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 standard string manipulation functions, enhancing readability and ensuring correct parsing of directives with values. A comprehensive test suite was also introduced to verify various Cache-Control header scenarios. Feedback suggests utilizing strings.SplitSeq to improve performance by avoiding unnecessary slice allocations.

}
}
if i+dirLen == ccLen || cc[i+dirLen] == ',' {
for _, part := range strings.Split(cc, ",") {
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

For better performance and to reduce allocations, consider using strings.SplitSeq instead of strings.Split. strings.SplitSeq provides an iterator over the substrings, avoiding the allocation of a slice to hold all of them at once. This is more memory-efficient, especially for Cache-Control headers with many directives.

This change would also be consistent with the implementation of parseVary in this file, which already uses strings.SplitSeq.

Suggested change
for _, part := range strings.Split(cc, ",") {
for part := range strings.SplitSeq(cc, ",") {

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

🧹 Nitpick comments (1)
middleware/cache/cache.go (1)

911-914: Switch to utils.TrimSpace for consistency

Lines 911 and 914 use strings.TrimSpace, but this file already uses utils.TrimSpace elsewhere (line 1253) and the project convention (from coding guidelines) requires preferring github.com/gofiber/utils/v2 helpers for common string operations. Update both calls to use utils.TrimSpace instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@middleware/cache/cache.go` around lines 911 - 914, Replace the two calls to
strings.TrimSpace in the parsing code (the Trim of variable part and the Trim of
variable key) with utils.TrimSpace (from github.com/gofiber/utils/v2) so they
match the file's existing convention; ensure the utils package is imported and
remove the strings import if it becomes unused. This affects the code that sets
part = ... and key = ... (use utils.TrimSpace(part) and utils.TrimSpace(key)) so
the behavior remains identical but follows project helper usage.
🤖 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 910-916: The loop that does strings.Split(cc, ",") can
misinterpret commas inside quoted directive values; replace this ad-hoc
comma-splitting with the RFC-aware tokenizer by calling
parseCacheControlDirectives(cc) and iterate its returned directives (or map of
key→value) instead of splitting, then use strings.EqualFold against the
directive variable to match keys (and use the provided value from
parseCacheControlDirectives when needed); update the logic in the function
containing the current loop so it consumes parseCacheControlDirectives' output
to avoid false matches from quoted commas.

---

Nitpick comments:
In `@middleware/cache/cache.go`:
- Around line 911-914: Replace the two calls to strings.TrimSpace in the parsing
code (the Trim of variable part and the Trim of variable key) with
utils.TrimSpace (from github.com/gofiber/utils/v2) so they match the file's
existing convention; ensure the utils package is imported and remove the strings
import if it becomes unused. This affects the code that sets part = ... and key
= ... (use utils.TrimSpace(part) and utils.TrimSpace(key)) so the behavior
remains identical but follows project helper usage.
🪄 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: 584c726d-92c1-4b97-b440-ed548b6b62db

📥 Commits

Reviewing files that changed from the base of the PR and between bf952a1 and 58cbd0d.

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

Comment on lines +910 to +916
for _, part := range strings.Split(cc, ",") {
part = strings.TrimSpace(part)

key, _, _ := strings.Cut(part, "=")
key = strings.TrimSpace(key)

if strings.EqualFold(key, directive) {
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.

⚠️ Potential issue | 🟡 Minor

Quoted commas can produce false directive matches

The current comma-split approach can misparse valid quoted directive values and report a directive that only appears inside a quoted string. Consider reusing parseCacheControlDirectives here for RFC-consistent tokenization.

Suggested fix
 func hasDirective(cc, directive string) bool {
-	for _, part := range strings.Split(cc, ",") {
-		part = strings.TrimSpace(part)
-
-		key, _, _ := strings.Cut(part, "=")
-		key = strings.TrimSpace(key)
-
-		if strings.EqualFold(key, directive) {
-			return true
-		}
-	}
-	return false
+	found := false
+	parseCacheControlDirectives(utils.UnsafeBytes(cc), func(key, _ []byte) {
+		if !found && utils.EqualFold(utils.UnsafeString(key), directive) {
+			found = true
+		}
+	})
+	return found
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for _, part := range strings.Split(cc, ",") {
part = strings.TrimSpace(part)
key, _, _ := strings.Cut(part, "=")
key = strings.TrimSpace(key)
if strings.EqualFold(key, directive) {
func hasDirective(cc, directive string) bool {
found := false
parseCacheControlDirectives(utils.UnsafeBytes(cc), func(key, _ []byte) {
if !found && utils.EqualFold(utils.UnsafeString(key), directive) {
found = true
}
})
return found
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@middleware/cache/cache.go` around lines 910 - 916, The loop that does
strings.Split(cc, ",") can misinterpret commas inside quoted directive values;
replace this ad-hoc comma-splitting with the RFC-aware tokenizer by calling
parseCacheControlDirectives(cc) and iterate its returned directives (or map of
key→value) instead of splitting, then use strings.EqualFold against the
directive variable to match keys (and use the provided value from
parseCacheControlDirectives when needed); update the logic in the function
containing the current loop so it consumes parseCacheControlDirectives' output
to avoid false matches from quoted commas.

@KambojRajan
Copy link
Copy Markdown
Author

@gaby
The previous hasDirective implementation only recognized a directive when followed by a comma or end-of-string. This meant valid cases like max-age=604800, no-cache="Set-Cookie", or private (trailing whitespace) were not detected. This could cause the cache middleware to serve responses it should have skipped, for example, a private response leaking into shared cache.
The new implementation splits the header by ,, trims whitespace, and uses strings.Cut to separate the directive name from its value. This correctly handles all valid Cache-Control directive formats per RFC 9111.

@KambojRajan
Copy link
Copy Markdown
Author

instead of using strings, I'll use simpler methods to achieve the same

@gaby
Copy link
Copy Markdown
Member

gaby commented Apr 4, 2026

@KambojRajan I appreciate it, but we already have someone working on this.

See #4144

@gaby gaby closed this Apr 4, 2026
@github-project-automation github-project-automation bot moved this to Done in v3 Apr 4, 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.

4 participants