Skip to content

Conversation

@ErfanMomeniii
Copy link

Description

Add ExpirationFunc configuration option to the Limiter middleware, enabling dynamic expiration duration per request. This mirrors the existing MaxFunc pattern and allows users to apply different rate-limiting windows based on request context.

Fixes (#3749)

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: No performance regression; expiration calculation moved inside handler (same pattern as existing MaxFunc).
  • Documentation Update: Updated /docs/middleware/limiter.md with ExpirationFunc field, config table entry, and usage example.
  • Changelog/What's New: Added ExpirationFunc option to Limiter middleware for dynamic per-request expiration duration.
  • Migration Guide: Not required - backward compatible, ExpirationFunc defaults to returning static Expiration value.
  • API Alignment with Express: N/A
  • API Longevity: Follows established MaxFunc pattern, ensuring consistency with existing API design.
  • Examples: Documentation includes a usage example for dynamic expiration based on route.

Type of change

  • New feature (non-breaking change which adds functionality)

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.

@ErfanMomeniii ErfanMomeniii requested a review from a team as a code owner January 4, 2026 16:56
@welcome
Copy link

welcome bot commented Jan 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
Contributor

coderabbitai bot commented Jan 4, 2026

Walkthrough

Adds ExpirationFunc to limiter.Config to compute per-request expirations; defaulting logic preserves previous behavior. Fixed and sliding window handlers call ExpirationFunc(c) and use the returned duration for TTL/persistence. Docs and tests updated for behavior and edge cases.

Changes

Cohort / File(s) Summary
Configuration & Docs
middleware/limiter/config.go, docs/middleware/limiter.md
Add public ExpirationFunc func(fiber.Ctx) time.Duration to Config; update ConfigDefault/configDefault to set a default closure when nil; document dynamic expiration and examples.
Fixed Window
middleware/limiter/limiter_fixed.go
Call expirationDuration := cfg.ExpirationFunc(c) per request and persist/store TTLs using that duration instead of static cfg.Expiration.
Sliding Window
middleware/limiter/limiter_sliding.go
Determine expirationDuration per request (with fallback), validate >0, and use its seconds for rotation/TTL instead of precomputed static value.
Tests
middleware/limiter/limiter_test.go
Add tests and a failing-storage test: verify ExpirationFunc overrides static expiration, fallback on zero/negative durations, and error propagation when storage Set fails during skip logic.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant Limiter as Limiter Middleware
  participant ExpFunc as ExpirationFunc
  participant Store as Backend Store
  participant App as App Handler

  Client->>Limiter: HTTP request
  Limiter->>ExpFunc: call cfg.ExpirationFunc(c)
  ExpFunc-->>Limiter: expirationDuration
  Limiter->>Store: read/update rate state (use expirationDuration for TTL)
  Store-->>Limiter: state (count, window, TTL)
  alt allowed
    Limiter->>App: forward request
    App-->>Client: 200 OK
  else blocked
    Limiter-->>Client: 429 Too Many Requests
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Possibly related PRs

Suggested labels

codex

Suggested reviewers

  • sixcolors
  • ReneWerner87
  • efectn

Poem

🐰 I nibble at the ticking clock,

Each request finds its own small block,
I hop and set a TTL fine,
Per-request rhythm, line by line,
A rabbit's dance in rate-limit time.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 64.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding ExpirationFunc feature to the limiter middleware for dynamic expiration.
Description check ✅ Passed The PR description follows the template structure with all required sections completed: description, changes introduced (with checklist items marked), type of change, and pre-submission checklist all properly addressed.

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

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cdedcf1 and 6505229.

📒 Files selected for processing (1)
  • middleware/limiter/limiter_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • middleware/limiter/limiter_test.go

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.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @ErfanMomeniii, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the Limiter middleware by introducing a flexible ExpirationFunc option. This allows developers to define a function that dynamically determines the rate-limiting window's expiration for each incoming request, moving beyond a static expiration period. This change provides greater control and adaptability for rate-limiting strategies, enabling more sophisticated per-request logic.

Highlights

  • Dynamic Expiration for Limiter Middleware: Introduced a new ExpirationFunc configuration option to the Limiter middleware, allowing the expiration duration for rate-limiting to be calculated dynamically per request based on the request context. This mirrors the existing MaxFunc pattern.
  • Documentation Updates: The official documentation (docs/middleware/limiter.md) has been updated to include details about the new ExpirationFunc, its configuration, and a usage example demonstrating dynamic expiration based on the request route.
  • Backward Compatibility: The new feature is fully backward compatible, as ExpirationFunc defaults to returning the static Expiration value if not explicitly provided, ensuring no breaking changes for existing implementations.
  • Comprehensive Testing: New unit tests have been added for both FixedWindow and SlidingWindow limiter types to verify that ExpirationFunc correctly overrides static expiration settings and functions as intended.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@ReneWerner87 ReneWerner87 added this to v3 Jan 4, 2026
@ReneWerner87 ReneWerner87 added this to the v3 milestone Jan 4, 2026
Copy link
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 introduces a new ExpirationFunc to the limiter middleware, allowing for dynamic expiration times per request. The implementation correctly mirrors the existing MaxFunc pattern, and the changes are well-contained across the configuration, middleware logic, and documentation. The new tests are also comprehensive. I have one suggestion to improve a code comment for better clarity and maintainability.

@codecov
Copy link

codecov bot commented Jan 4, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.11%. Comparing base (a7c2862) to head (6505229).
⚠️ Report is 31 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3984      +/-   ##
==========================================
- Coverage   91.60%   91.11%   -0.50%     
==========================================
  Files         119      119              
  Lines       10262    10859     +597     
==========================================
+ Hits         9401     9894     +493     
- Misses        544      610      +66     
- Partials      317      355      +38     
Flag Coverage Δ
unittests 91.11% <100.00%> (-0.50%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
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

This PR adds an ExpirationFunc configuration option to the Limiter middleware, enabling dynamic expiration duration per request based on request context. This mirrors the existing MaxFunc pattern for consistency and allows more flexible rate-limiting strategies.

Key changes:

  • Added ExpirationFunc field to Config struct with appropriate defaults
  • Updated both FixedWindow and SlidingWindow implementations to use dynamic expiration
  • Added comprehensive tests for both limiter types with the new functionality

Reviewed changes

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

Show a summary per file
File Description
middleware/limiter/config.go Added ExpirationFunc field with default implementation and configuration logic
middleware/limiter/limiter_fixed.go Updated to calculate expiration per-request and use it in storage operations
middleware/limiter/limiter_sliding.go Updated to calculate expiration per-request for sliding window algorithm
middleware/limiter/limiter_test.go Added tests validating ExpirationFunc overrides static Expiration for both window types
docs/middleware/limiter.md Added documentation for ExpirationFunc field and usage example

return 20
},
Expiration: 30 * time.Second,
ExpirationFunc: func(c fiber.Ctx) time.Duration {
Copy link

Copilot AI Jan 4, 2026

Choose a reason for hiding this comment

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

The ExpirationFunc in this example is redundant because it returns the same value as the static Expiration field above it (30 * time.Second). For a more useful demonstration, consider showing either a different value or a dynamic calculation based on the request context, similar to the dynamic expiration example below.

Suggested change
ExpirationFunc: func(c fiber.Ctx) time.Duration {
ExpirationFunc: func(c fiber.Ctx) time.Duration {
if c.Path() == "/login" {
return 60 * time.Second
}

Copilot uses AI. Check for mistakes.
@gaby
Copy link
Member

gaby commented Jan 4, 2026

@codex review

@gaby
Copy link
Member

gaby commented Jan 4, 2026

@ErfanMomeniii Run make format to fix the lint issue

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8dc53410ab

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
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: 2

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8dc5341 and 65dab38.

📒 Files selected for processing (2)
  • middleware/limiter/limiter_fixed.go
  • middleware/limiter/limiter_sliding.go
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Prefer github.com/gofiber/utils/v2 helpers (for example, utils.Trim) when performing common operations such as string manipulation, whenever it is practical and appropriate for the surrounding code

Files:

  • middleware/limiter/limiter_fixed.go
  • middleware/limiter/limiter_sliding.go
🔇 Additional comments (4)
middleware/limiter/limiter_sliding.go (2)

24-25: Good refactoring to enable dynamic expiration.

Removing the static expiration initialization in favor of per-request computation is the correct approach to support the new ExpirationFunc feature.


43-45: Excellent implementation of per-request expiration.

The pattern correctly computes expirationDuration once per request and reuses it consistently throughout the request lifecycle, including in the post-response path. This ensures the same expiration window applies to all operations within a single request, which is the correct behavior.

Also applies to: 66-66, 73-73, 93-93, 134-134, 138-138, 145-145

middleware/limiter/limiter_fixed.go (2)

22-23: Good refactoring to enable dynamic expiration.

Consistent with the sliding window implementation, removing the static expiration initialization is the correct approach for supporting per-request dynamic expiration.


41-43: Well-implemented per-request expiration for fixed window.

The implementation correctly follows the same pattern as the sliding window and MaxFunc, computing the expiration once per request and using it consistently throughout. The fixed window logic properly applies the dynamic expiration.

Also applies to: 65-65, 69-69, 82-82, 122-122

Comment on lines 41 to 43
// Generate expiration from generator
expirationDuration := cfg.ExpirationFunc(c)
expiration := uint64(expirationDuration.Seconds())
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Same duration validation needed here.

The same conversion safety concerns from limiter_sliding.go apply here. Please add validation to ensure ExpirationFunc returns a positive, reasonable duration before converting to uint64.

🔎 Suggested validation
 // Generate expiration from generator
 expirationDuration := cfg.ExpirationFunc(c)
+if expirationDuration <= 0 {
+	return fmt.Errorf("limiter: ExpirationFunc must return a positive duration, got %v", expirationDuration)
+}
 expiration := uint64(expirationDuration.Seconds())
📝 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
// Generate expiration from generator
expirationDuration := cfg.ExpirationFunc(c)
expiration := uint64(expirationDuration.Seconds())
// Generate expiration from generator
expirationDuration := cfg.ExpirationFunc(c)
if expirationDuration <= 0 {
return fmt.Errorf("limiter: ExpirationFunc must return a positive duration, got %v", expirationDuration)
}
expiration := uint64(expirationDuration.Seconds())
🤖 Prompt for AI Agents
In middleware/limiter/limiter_fixed.go around lines 41-43, validate the duration
returned by cfg.ExpirationFunc(c) before converting to uint64: ensure the
duration is positive (>0) and clamp it to a safe upper bound to avoid overflow
when calling Seconds(); if the duration is <=0, set a sensible default (e.g.,
1s) and log or warn; if Seconds() exceeds the max uint64 value, clamp to
math.MaxUint64 (or a defined max) before casting to uint64. Implement these
checks immediately after calling ExpirationFunc and use the validated/clamped
value for expiration.

@ErfanMomeniii
Copy link
Author

@ErfanMomeniii Run make format to fix the lint issue
Done @gaby

@ReneWerner87
Copy link
Member

@ErfanMomeniii thx for this feature
pls check the ai review hints

@ErfanMomeniii
Copy link
Author

@ErfanMomeniii thx for this feature pls check the ai review hints

Done @ReneWerner87

@ErfanMomeniii
Copy link
Author

I have added tests to increase coverage.
@gaby @ReneWerner87

@gaby gaby moved this to In Progress in v3 Jan 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

3 participants