Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions middleware/limiter/limiter_sliding.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ func (SlidingWindow) New(cfg *Config) fiber.Handler {
expirationDuration = ConfigDefault.Expiration
}
expiration := uint64(expirationDuration.Seconds())
// Sub-second expirations truncate to 0 seconds, which would make the
// window weight a division by zero (NaN rate -> every request is
// rejected). Floor the window to 1 second.
if expiration == 0 {
expiration = 1
}

// Get key from request
key := cfg.KeyGenerator(c)
Expand Down
22 changes: 22 additions & 0 deletions middleware/limiter/limiter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1863,3 +1863,25 @@
cfg.clock = func() time.Time { return time.Unix(42, 0) }
require.Equal(t, uint64(42), cfg.currentSecond())
}

// TestLimiterSlidingSubSecondExpiration is a regression test: a sub-second
// ExpirationFunc truncated to 0 seconds via uint64(d.Seconds()), so the
// sliding window weight became a division by zero (NaN rate) and every
// request was rejected. The window must floor to 1 second: requests within
// Max are admitted.
func TestLimiterSlidingSubSecondExpiration(t *testing.T) {
t.Parallel()

app := fiber.New()
app.Use(New(Config{
Max: 5,
LimiterMiddleware: SlidingWindow{},
ExpirationFunc: func(fiber.Ctx) time.Duration { return 500 * time.Millisecond },
}))
app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello tester!") })

// Without the fix the very first request is wrongly rejected with 429.
resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))

Check failure on line 1884 in middleware/limiter/limiter_test.go

View workflow job for this annotation

GitHub Actions / lint / lint

httpNoBody: http.NoBody should be preferred to the nil request body (gocritic)
require.NoError(t, err)
require.Equal(t, fiber.StatusOK, resp.StatusCode)
}
Loading