Skip to content
Merged
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
7 changes: 6 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ async function fastifyRateLimit (fastify, settings) {
? settings.timeWindow
: 1000 * 60

globalParams.timeWindowInSeconds = (globalParams.timeWindow / 1000) | 0

globalParams.hook = settings.hook || defaultHook
globalParams.allowList = settings.allowList || settings.whitelist || null
globalParams.ban = settings.ban || null
Expand Down Expand Up @@ -154,6 +156,9 @@ async function fastifyRateLimit (fastify, settings) {
if (typeof result.timeWindow === 'string') {
result.timeWindow = ms(result.timeWindow)
}
if (typeof result.timeWindow === 'number') {
result.timeWindowInSeconds = (result.timeWindow / 1000) | 0
}
return result
}
}
Expand Down Expand Up @@ -246,7 +251,7 @@ function rateLimitRequestHandler (params, pluginComponent) {
if (params.addHeaders[params.labels.rateRemaining]) { res.header(params.labels.rateRemaining, 0) }
if (params.addHeaders[params.labels.rateReset]) { res.header(params.labels.rateReset, timeLeft) }
if (params.addHeaders[params.labels.retryAfter]) {
const resetAfterTime = (params.enableDraftSpec) ? timeLeft : params.timeWindow
const resetAfterTime = (params.enableDraftSpec ? timeLeft : params.timeWindowInSeconds)
res.header(params.labels.retryAfter, resetAfterTime)
}

Expand Down
16 changes: 8 additions & 8 deletions test/global-rate-limit.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ test('Basic', async t => {
t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
t.equal(res.headers['x-ratelimit-limit'], '2')
t.equal(res.headers['x-ratelimit-remaining'], '0')
t.equal(res.headers['retry-after'], '1000')
t.equal(res.headers['retry-after'], '1')
t.same({
statusCode: 429,
error: 'Too Many Requests',
Expand Down Expand Up @@ -86,7 +86,7 @@ test('With text timeWindow', async t => {
t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
t.equal(res.headers['x-ratelimit-limit'], '2')
t.equal(res.headers['x-ratelimit-remaining'], '0')
t.equal(res.headers['retry-after'], '1000')
t.equal(res.headers['retry-after'], '1')
t.same({
statusCode: 429,
error: 'Too Many Requests',
Expand Down Expand Up @@ -416,7 +416,7 @@ test('With redis store', async t => {
t.equal(res.headers['x-ratelimit-limit'], '2')
t.equal(res.headers['x-ratelimit-remaining'], '0')
t.equal(res.headers['x-ratelimit-reset'], '0')
t.equal(res.headers['retry-after'], '1000')
t.equal(res.headers['retry-after'], '1')
t.same({
statusCode: 429,
error: 'Too Many Requests',
Expand Down Expand Up @@ -541,7 +541,7 @@ test('With keyGenerator', async t => {
t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
t.equal(res.headers['x-ratelimit-limit'], '2')
t.equal(res.headers['x-ratelimit-remaining'], '0')
t.equal(res.headers['retry-after'], '1000')
t.equal(res.headers['retry-after'], '1')
t.same({
statusCode: 429,
error: 'Too Many Requests',
Expand Down Expand Up @@ -594,7 +594,7 @@ test('With async/await keyGenerator', async t => {
t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
t.equal(res.headers['x-ratelimit-limit'], '1')
t.equal(res.headers['x-ratelimit-remaining'], '0')
t.equal(res.headers['retry-after'], '1000')
t.equal(res.headers['retry-after'], '1')
t.same({
statusCode: 429,
error: 'Too Many Requests',
Expand Down Expand Up @@ -660,7 +660,7 @@ test('With CustomStore', async t => {
t.equal(res.headers['x-ratelimit-limit'], '2')
t.equal(res.headers['x-ratelimit-remaining'], '0')
t.equal(res.headers['x-ratelimit-reset'], '7')
t.equal(res.headers['retry-after'], '10000')
t.equal(res.headers['retry-after'], '10')
t.same({
statusCode: 429,
error: 'Too Many Requests',
Expand Down Expand Up @@ -862,7 +862,7 @@ test('hide rate limit headers on exceeding', async t => {
t.equal(res.headers['x-ratelimit-limit'], '1')
t.equal(res.headers['x-ratelimit-remaining'], '0')
t.not(res.headers['x-ratelimit-reset'], undefined)
t.equal(res.headers['retry-after'], '1000')
t.equal(res.headers['retry-after'], '1')

t.context.clock.tick(1100)

Expand Down Expand Up @@ -1276,7 +1276,7 @@ test('When use a custom nameSpace', async t => {
t.equal(res.headers['x-ratelimit-limit'], '2')
t.equal(res.headers['x-ratelimit-remaining'], '0')
t.ok(['0', '1'].includes(res.headers['x-ratelimit-reset']))
t.equal(res.headers['retry-after'], '1000')
t.equal(res.headers['retry-after'], '1')
t.same({
statusCode: 429,
error: 'Too Many Requests',
Expand Down
4 changes: 2 additions & 2 deletions test/not-found-handler-rate-limited.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ test('Set not found handler can be rate limited', async t => {
t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
t.equal(res.headers['x-ratelimit-limit'], '2')
t.equal(res.headers['x-ratelimit-remaining'], '0')
t.equal(res.headers['retry-after'], '1000')
t.equal(res.headers['retry-after'], '1')
t.equal(res.headers['x-ratelimit-reset'], '0')
t.same(JSON.parse(res.payload), {
statusCode: 429,
Expand Down Expand Up @@ -95,7 +95,7 @@ test('Set not found handler can be rate limited with specific options', async t
t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
t.equal(res.headers['x-ratelimit-limit'], '4')
t.equal(res.headers['x-ratelimit-remaining'], '0')
t.equal(res.headers['retry-after'], '2000')
t.equal(res.headers['retry-after'], '2')
t.equal(res.headers['x-ratelimit-reset'], '1')
t.same(JSON.parse(res.payload), {
statusCode: 429,
Expand Down
24 changes: 12 additions & 12 deletions test/route-rate-limit.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ test('Basic', async t => {
t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
t.equal(res.headers['x-ratelimit-limit'], '2')
t.equal(res.headers['x-ratelimit-remaining'], '0')
t.equal(res.headers['retry-after'], '1000')
t.equal(res.headers['retry-after'], '1')
t.equal(res.headers['x-ratelimit-reset'], '0')
t.same({
statusCode: 429,
Expand Down Expand Up @@ -110,7 +110,7 @@ test('With text timeWindow', async t => {
t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
t.equal(res.headers['x-ratelimit-limit'], '2')
t.equal(res.headers['x-ratelimit-remaining'], '0')
t.equal(res.headers['retry-after'], '1000')
t.equal(res.headers['retry-after'], '1')
t.same(JSON.parse(res.payload), {
statusCode: 429,
error: 'Too Many Requests',
Expand Down Expand Up @@ -289,7 +289,7 @@ test('With redis store', async t => {
t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
t.equal(res.headers['x-ratelimit-limit'], '2')
t.equal(res.headers['x-ratelimit-remaining'], '0')
t.equal(res.headers['retry-after'], '1000')
t.equal(res.headers['retry-after'], '1')
t.equal(res.headers['x-ratelimit-reset'], '0')
t.same({
statusCode: 429,
Expand Down Expand Up @@ -426,7 +426,7 @@ test('With keyGenerator', async t => {
t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
t.equal(res.headers['x-ratelimit-limit'], '2')
t.equal(res.headers['x-ratelimit-remaining'], '0')
t.equal(res.headers['retry-after'], '1000')
t.equal(res.headers['retry-after'], '1')
t.same({
statusCode: 429,
error: 'Too Many Requests',
Expand Down Expand Up @@ -631,7 +631,7 @@ test('custom error response', async t => {
t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
t.equal(res.headers['x-ratelimit-limit'], '2')
t.equal(res.headers['x-ratelimit-remaining'], '0')
t.equal(res.headers['retry-after'], '1000')
t.equal(res.headers['retry-after'], '1')
t.same(JSON.parse(res.payload), {
statusCode: 429,
timeWindow: '1 second',
Expand Down Expand Up @@ -809,7 +809,7 @@ test('hide rate limit headers on exceeding', async t => {
t.equal(res.headers['x-ratelimit-limit'], '1')
t.equal(res.headers['x-ratelimit-remaining'], '0')
t.not(res.headers['x-ratelimit-reset'], undefined)
t.equal(res.headers['retry-after'], '1000')
t.equal(res.headers['retry-after'], '1')

t.context.clock.tick(1100)

Expand Down Expand Up @@ -1015,7 +1015,7 @@ test('With CustomStore', async t => {
t.equal(res.headers['x-ratelimit-limit'], '2')
t.equal(res.headers['x-ratelimit-remaining'], '0')
t.equal(res.headers['x-ratelimit-reset'], '7')
t.equal(res.headers['retry-after'], '10000')
t.equal(res.headers['retry-after'], '10')
t.same({
statusCode: 429,
error: 'Too Many Requests',
Expand Down Expand Up @@ -1118,7 +1118,7 @@ test('Allow multiple different rate limiter registrations', async t => {
t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
t.equal(res.headers['x-ratelimit-limit'], '1')
t.equal(res.headers['x-ratelimit-remaining'], '0')
t.equal(res.headers['retry-after'], '1000')
t.equal(res.headers['retry-after'], '1')

res = await fastify.inject('/test')
t.equal(res.statusCode, 200)
Expand All @@ -1130,7 +1130,7 @@ test('Allow multiple different rate limiter registrations', async t => {
t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
t.equal(res.headers['x-ratelimit-limit'], '1')
t.equal(res.headers['x-ratelimit-remaining'], '0')
t.equal(res.headers['retry-after'], '1000')
t.equal(res.headers['retry-after'], '1')
})

test('With enable IETF draft spec', async t => {
Expand Down Expand Up @@ -1235,7 +1235,7 @@ test('Allow custom timeWindow in preHandler', async t => {
t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
t.equal(res.headers['x-ratelimit-limit'], '1')
t.equal(res.headers['x-ratelimit-remaining'], '0')
t.equal(res.headers['retry-after'], '120000')
t.equal(res.headers['retry-after'], '120')

res = await fastify.inject('/3')
t.equal(res.statusCode, 200)
Expand All @@ -1247,15 +1247,15 @@ test('Allow custom timeWindow in preHandler', async t => {
t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
t.equal(res.headers['x-ratelimit-limit'], '1')
t.equal(res.headers['x-ratelimit-remaining'], '0')
t.equal(res.headers['retry-after'], '180000')
t.equal(res.headers['retry-after'], '180')

res = await fastify.inject('/default')
t.equal(res.statusCode, 200)
t.equal(res.headers['x-ratelimit-limit'], '1')
t.equal(res.headers['x-ratelimit-remaining'], '0')

res = await fastify.inject('/default')
t.equal(res.headers['retry-after'], '10000')
t.equal(res.headers['retry-after'], '10')
t.equal(res.statusCode, 429)
})

Expand Down