Aktueller Stand
This commit is contained in:
224
backend/node_modules/@fastify/rate-limit/test/create-rate-limit.test.js
generated
vendored
Normal file
224
backend/node_modules/@fastify/rate-limit/test/create-rate-limit.test.js
generated
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
'use strict'
|
||||
|
||||
const { test, mock } = require('node:test')
|
||||
const Fastify = require('fastify')
|
||||
const rateLimit = require('../index')
|
||||
|
||||
test('With global rate limit options', async t => {
|
||||
t.plan(8)
|
||||
const clock = mock.timers
|
||||
clock.enable(0)
|
||||
const fastify = Fastify()
|
||||
await fastify.register(rateLimit, {
|
||||
global: false,
|
||||
max: 2,
|
||||
timeWindow: 1000
|
||||
})
|
||||
|
||||
const checkRateLimit = fastify.createRateLimit()
|
||||
|
||||
fastify.get('/', async (req, reply) => {
|
||||
const limit = await checkRateLimit(req)
|
||||
return limit
|
||||
})
|
||||
|
||||
let res
|
||||
|
||||
res = await fastify.inject('/')
|
||||
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.json(), {
|
||||
isAllowed: false,
|
||||
key: '127.0.0.1',
|
||||
max: 2,
|
||||
timeWindow: 1000,
|
||||
remaining: 1,
|
||||
ttl: 1000,
|
||||
ttlInSeconds: 1,
|
||||
isExceeded: false,
|
||||
isBanned: false
|
||||
})
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.json(), {
|
||||
isAllowed: false,
|
||||
key: '127.0.0.1',
|
||||
max: 2,
|
||||
timeWindow: 1000,
|
||||
remaining: 0,
|
||||
ttl: 1000,
|
||||
ttlInSeconds: 1,
|
||||
isExceeded: false,
|
||||
isBanned: false
|
||||
})
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.json(), {
|
||||
isAllowed: false,
|
||||
key: '127.0.0.1',
|
||||
max: 2,
|
||||
timeWindow: 1000,
|
||||
remaining: 0,
|
||||
ttl: 1000,
|
||||
ttlInSeconds: 1,
|
||||
isExceeded: true,
|
||||
isBanned: false
|
||||
})
|
||||
|
||||
clock.tick(1100)
|
||||
|
||||
res = await fastify.inject('/')
|
||||
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.json(), {
|
||||
isAllowed: false,
|
||||
key: '127.0.0.1',
|
||||
max: 2,
|
||||
timeWindow: 1000,
|
||||
remaining: 1,
|
||||
ttl: 1000,
|
||||
ttlInSeconds: 1,
|
||||
isExceeded: false,
|
||||
isBanned: false
|
||||
})
|
||||
|
||||
clock.reset()
|
||||
})
|
||||
|
||||
test('With custom rate limit options', async t => {
|
||||
t.plan(10)
|
||||
const clock = mock.timers
|
||||
clock.enable(0)
|
||||
const fastify = Fastify()
|
||||
await fastify.register(rateLimit, {
|
||||
global: false,
|
||||
max: 5,
|
||||
timeWindow: 1000
|
||||
})
|
||||
|
||||
const checkRateLimit = fastify.createRateLimit({
|
||||
max: 2,
|
||||
timeWindow: 1000,
|
||||
ban: 1
|
||||
})
|
||||
|
||||
fastify.get('/', async (req, reply) => {
|
||||
const limit = await checkRateLimit(req)
|
||||
return limit
|
||||
})
|
||||
|
||||
let res
|
||||
|
||||
res = await fastify.inject('/')
|
||||
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.json(), {
|
||||
isAllowed: false,
|
||||
key: '127.0.0.1',
|
||||
max: 2,
|
||||
timeWindow: 1000,
|
||||
remaining: 1,
|
||||
ttl: 1000,
|
||||
ttlInSeconds: 1,
|
||||
isExceeded: false,
|
||||
isBanned: false
|
||||
})
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.json(), {
|
||||
isAllowed: false,
|
||||
key: '127.0.0.1',
|
||||
max: 2,
|
||||
timeWindow: 1000,
|
||||
remaining: 0,
|
||||
ttl: 1000,
|
||||
ttlInSeconds: 1,
|
||||
isExceeded: false,
|
||||
isBanned: false
|
||||
})
|
||||
|
||||
// should be exceeded now
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.json(), {
|
||||
isAllowed: false,
|
||||
key: '127.0.0.1',
|
||||
max: 2,
|
||||
timeWindow: 1000,
|
||||
remaining: 0,
|
||||
ttl: 1000,
|
||||
ttlInSeconds: 1,
|
||||
isExceeded: true,
|
||||
isBanned: false
|
||||
})
|
||||
|
||||
// should be banned now
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.json(), {
|
||||
isAllowed: false,
|
||||
key: '127.0.0.1',
|
||||
max: 2,
|
||||
timeWindow: 1000,
|
||||
remaining: 0,
|
||||
ttl: 1000,
|
||||
ttlInSeconds: 1,
|
||||
isExceeded: true,
|
||||
isBanned: true
|
||||
})
|
||||
|
||||
clock.tick(1100)
|
||||
|
||||
res = await fastify.inject('/')
|
||||
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.json(), {
|
||||
isAllowed: false,
|
||||
key: '127.0.0.1',
|
||||
max: 2,
|
||||
timeWindow: 1000,
|
||||
remaining: 1,
|
||||
ttl: 1000,
|
||||
ttlInSeconds: 1,
|
||||
isExceeded: false,
|
||||
isBanned: false
|
||||
})
|
||||
|
||||
clock.reset()
|
||||
})
|
||||
|
||||
test('With allow list', async t => {
|
||||
t.plan(2)
|
||||
const clock = mock.timers
|
||||
clock.enable(0)
|
||||
const fastify = Fastify()
|
||||
await fastify.register(rateLimit, {
|
||||
global: false,
|
||||
max: 5,
|
||||
timeWindow: 1000
|
||||
})
|
||||
|
||||
const checkRateLimit = fastify.createRateLimit({
|
||||
allowList: ['127.0.0.1'],
|
||||
max: 2,
|
||||
timeWindow: 1000
|
||||
})
|
||||
|
||||
fastify.get('/', async (req, reply) => {
|
||||
const limit = await checkRateLimit(req)
|
||||
return limit
|
||||
})
|
||||
|
||||
const res = await fastify.inject('/')
|
||||
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
|
||||
// expect a different return type because isAllowed is true
|
||||
t.assert.deepStrictEqual(res.json(), {
|
||||
isAllowed: true,
|
||||
key: '127.0.0.1'
|
||||
})
|
||||
})
|
||||
232
backend/node_modules/@fastify/rate-limit/test/exponential-backoff.test.js
generated
vendored
Normal file
232
backend/node_modules/@fastify/rate-limit/test/exponential-backoff.test.js
generated
vendored
Normal file
@@ -0,0 +1,232 @@
|
||||
'use strict'
|
||||
const { test } = require('node:test')
|
||||
const assert = require('node:assert')
|
||||
const Fastify = require('fastify')
|
||||
const rateLimit = require('../index')
|
||||
|
||||
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
||||
|
||||
test('Exponential Backoff', async () => {
|
||||
const fastify = Fastify()
|
||||
|
||||
// Register rate limit plugin with exponentialBackoff set to true in routeConfig
|
||||
await fastify.register(rateLimit, { max: 2, timeWindow: 500 })
|
||||
|
||||
fastify.get(
|
||||
'/expoential-backoff',
|
||||
{
|
||||
config: {
|
||||
rateLimit: {
|
||||
max: 2,
|
||||
timeWindow: 500,
|
||||
exponentialBackoff: true
|
||||
}
|
||||
}
|
||||
},
|
||||
async () => 'exponential backoff applied!'
|
||||
)
|
||||
|
||||
// Test
|
||||
const res = await fastify.inject({ url: '/expoential-backoff', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 200)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1')
|
||||
|
||||
const res2 = await fastify.inject({ url: '/expoential-backoff', method: 'GET' })
|
||||
assert.deepStrictEqual(res2.statusCode, 200)
|
||||
assert.deepStrictEqual(res2.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(res2.headers['x-ratelimit-remaining'], '0')
|
||||
|
||||
const res3 = await fastify.inject({ url: '/expoential-backoff', method: 'GET' })
|
||||
assert.deepStrictEqual(res3.statusCode, 429)
|
||||
assert.deepStrictEqual(res3.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(res3.headers['x-ratelimit-remaining'], '0')
|
||||
assert.deepStrictEqual(
|
||||
{
|
||||
statusCode: 429,
|
||||
error: 'Too Many Requests',
|
||||
message: 'Rate limit exceeded, retry in 1 second'
|
||||
},
|
||||
JSON.parse(res3.payload)
|
||||
)
|
||||
|
||||
const res4 = await fastify.inject({ url: '/expoential-backoff', method: 'GET' })
|
||||
assert.deepStrictEqual(res4.statusCode, 429)
|
||||
assert.deepStrictEqual(res4.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(res4.headers['x-ratelimit-remaining'], '0')
|
||||
assert.deepStrictEqual(
|
||||
{
|
||||
statusCode: 429,
|
||||
error: 'Too Many Requests',
|
||||
message: 'Rate limit exceeded, retry in 1 second'
|
||||
},
|
||||
JSON.parse(res4.payload)
|
||||
)
|
||||
|
||||
// Wait for the window to reset
|
||||
await sleep(1000)
|
||||
const res5 = await fastify.inject({ url: '/expoential-backoff', method: 'GET' })
|
||||
assert.deepStrictEqual(res5.statusCode, 200)
|
||||
assert.deepStrictEqual(res5.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(res5.headers['x-ratelimit-remaining'], '1')
|
||||
})
|
||||
|
||||
test('Global Exponential Backoff', async () => {
|
||||
const fastify = Fastify()
|
||||
|
||||
// Register rate limit plugin with exponentialBackoff set to true in routeConfig
|
||||
await fastify.register(rateLimit, { max: 2, timeWindow: 500, exponentialBackoff: true })
|
||||
|
||||
fastify.get(
|
||||
'/expoential-backoff-global',
|
||||
{
|
||||
config: {
|
||||
rateLimit: {
|
||||
max: 2,
|
||||
timeWindow: 500
|
||||
}
|
||||
}
|
||||
},
|
||||
async () => 'exponential backoff applied!'
|
||||
)
|
||||
|
||||
// Test
|
||||
let res
|
||||
res = await fastify.inject({ url: '/expoential-backoff-global', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 200)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1')
|
||||
|
||||
res = await fastify.inject({ url: '/expoential-backoff-global', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 200)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
|
||||
res = await fastify.inject({ url: '/expoential-backoff-global', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 429)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(
|
||||
{
|
||||
statusCode: 429,
|
||||
error: 'Too Many Requests',
|
||||
message: 'Rate limit exceeded, retry in 1 second'
|
||||
},
|
||||
JSON.parse(res.payload)
|
||||
)
|
||||
|
||||
res = await fastify.inject({ url: '/expoential-backoff-global', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 429)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(
|
||||
{
|
||||
statusCode: 429,
|
||||
error: 'Too Many Requests',
|
||||
message: 'Rate limit exceeded, retry in 1 second'
|
||||
},
|
||||
JSON.parse(res.payload)
|
||||
)
|
||||
|
||||
res = await fastify.inject({ url: '/expoential-backoff-global', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 429)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(
|
||||
{
|
||||
statusCode: 429,
|
||||
error: 'Too Many Requests',
|
||||
message: 'Rate limit exceeded, retry in 2 seconds'
|
||||
},
|
||||
JSON.parse(res.payload)
|
||||
)
|
||||
|
||||
res = await fastify.inject({ url: '/expoential-backoff-global', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 429)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(
|
||||
{
|
||||
statusCode: 429,
|
||||
error: 'Too Many Requests',
|
||||
message: 'Rate limit exceeded, retry in 4 seconds'
|
||||
},
|
||||
JSON.parse(res.payload)
|
||||
)
|
||||
})
|
||||
|
||||
test('MAx safe Exponential Backoff', async () => {
|
||||
const fastify = Fastify()
|
||||
|
||||
// Register rate limit plugin with exponentialBackoff set to true in routeConfig
|
||||
await fastify.register(rateLimit, { max: 2, timeWindow: 500, exponentialBackoff: true })
|
||||
|
||||
fastify.get(
|
||||
'/expoential-backoff-global',
|
||||
{
|
||||
config: {
|
||||
rateLimit: {
|
||||
max: 2,
|
||||
timeWindow: '285421 years'
|
||||
}
|
||||
}
|
||||
},
|
||||
async () => 'exponential backoff applied!'
|
||||
)
|
||||
|
||||
// Test
|
||||
let res
|
||||
res = await fastify.inject({ url: '/expoential-backoff-global', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 200)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1')
|
||||
|
||||
res = await fastify.inject({ url: '/expoential-backoff-global', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 200)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
|
||||
res = await fastify.inject({ url: '/expoential-backoff-global', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 429)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(
|
||||
{
|
||||
statusCode: 429,
|
||||
error: 'Too Many Requests',
|
||||
message: 'Rate limit exceeded, retry in 285421 years'
|
||||
},
|
||||
JSON.parse(res.payload)
|
||||
)
|
||||
|
||||
res = await fastify.inject({ url: '/expoential-backoff-global', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 429)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(
|
||||
{
|
||||
statusCode: 429,
|
||||
error: 'Too Many Requests',
|
||||
message: 'Rate limit exceeded, retry in 285421 years'
|
||||
},
|
||||
JSON.parse(res.payload)
|
||||
)
|
||||
|
||||
res = await fastify.inject({ url: '/expoential-backoff-global', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 429)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(
|
||||
{
|
||||
statusCode: 429,
|
||||
error: 'Too Many Requests',
|
||||
message: 'Rate limit exceeded, retry in 285421 years'
|
||||
},
|
||||
JSON.parse(res.payload)
|
||||
)
|
||||
|
||||
res = await fastify.inject({ url: '/expoential-backoff-global', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 429)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(
|
||||
{
|
||||
statusCode: 429,
|
||||
error: 'Too Many Requests',
|
||||
message: 'Rate limit exceeded, retry in 285421 years'
|
||||
},
|
||||
JSON.parse(res.payload)
|
||||
)
|
||||
})
|
||||
120
backend/node_modules/@fastify/rate-limit/test/github-issues/issue-207.test.js
generated
vendored
Normal file
120
backend/node_modules/@fastify/rate-limit/test/github-issues/issue-207.test.js
generated
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
'use strict'
|
||||
|
||||
const { test, mock } = require('node:test')
|
||||
const Fastify = require('fastify')
|
||||
const rateLimit = require('../../index')
|
||||
|
||||
test('issue #207 - when continueExceeding is true and the store is local then it should reset the rate-limit', async (t) => {
|
||||
const clock = mock.timers
|
||||
clock.enable()
|
||||
const fastify = Fastify()
|
||||
|
||||
await fastify.register(rateLimit, {
|
||||
global: false
|
||||
})
|
||||
|
||||
fastify.get(
|
||||
'/',
|
||||
{
|
||||
config: {
|
||||
rateLimit: {
|
||||
max: 1,
|
||||
timeWindow: 5000,
|
||||
continueExceeding: true
|
||||
}
|
||||
}
|
||||
},
|
||||
async () => {
|
||||
return 'hello!'
|
||||
}
|
||||
)
|
||||
|
||||
const firstOkResponse = await fastify.inject({
|
||||
url: '/',
|
||||
method: 'GET'
|
||||
})
|
||||
const firstRateLimitResponse = await fastify.inject({
|
||||
url: '/',
|
||||
method: 'GET'
|
||||
})
|
||||
|
||||
clock.tick(3000)
|
||||
|
||||
const secondRateLimitWithResettingTheRateLimitTimer = await fastify.inject({
|
||||
url: '/',
|
||||
method: 'GET'
|
||||
})
|
||||
|
||||
// after this the total time passed is 6s which WITHOUT `continueExceeding` the next request should be OK
|
||||
clock.tick(3000)
|
||||
|
||||
const thirdRateLimitWithResettingTheRateLimitTimer = await fastify.inject({
|
||||
url: '/',
|
||||
method: 'GET'
|
||||
})
|
||||
|
||||
// After this the rate limiter should allow for new requests
|
||||
clock.tick(5000)
|
||||
|
||||
const okResponseAfterRateLimitCompleted = await fastify.inject({
|
||||
url: '/',
|
||||
method: 'GET'
|
||||
})
|
||||
|
||||
t.assert.deepStrictEqual(firstOkResponse.statusCode, 200)
|
||||
|
||||
t.assert.deepStrictEqual(firstRateLimitResponse.statusCode, 429)
|
||||
t.assert.deepStrictEqual(
|
||||
firstRateLimitResponse.headers['x-ratelimit-limit'],
|
||||
'1'
|
||||
)
|
||||
t.assert.deepStrictEqual(
|
||||
firstRateLimitResponse.headers['x-ratelimit-remaining'],
|
||||
'0'
|
||||
)
|
||||
t.assert.deepStrictEqual(
|
||||
firstRateLimitResponse.headers['x-ratelimit-reset'],
|
||||
'5'
|
||||
)
|
||||
|
||||
t.assert.deepStrictEqual(
|
||||
secondRateLimitWithResettingTheRateLimitTimer.statusCode,
|
||||
429
|
||||
)
|
||||
t.assert.deepStrictEqual(
|
||||
secondRateLimitWithResettingTheRateLimitTimer.headers['x-ratelimit-limit'],
|
||||
'1'
|
||||
)
|
||||
t.assert.deepStrictEqual(
|
||||
secondRateLimitWithResettingTheRateLimitTimer.headers[
|
||||
'x-ratelimit-remaining'
|
||||
],
|
||||
'0'
|
||||
)
|
||||
t.assert.deepStrictEqual(
|
||||
secondRateLimitWithResettingTheRateLimitTimer.headers['x-ratelimit-reset'],
|
||||
'5'
|
||||
)
|
||||
|
||||
t.assert.deepStrictEqual(
|
||||
thirdRateLimitWithResettingTheRateLimitTimer.statusCode,
|
||||
429
|
||||
)
|
||||
t.assert.deepStrictEqual(
|
||||
thirdRateLimitWithResettingTheRateLimitTimer.headers['x-ratelimit-limit'],
|
||||
'1'
|
||||
)
|
||||
t.assert.deepStrictEqual(
|
||||
thirdRateLimitWithResettingTheRateLimitTimer.headers[
|
||||
'x-ratelimit-remaining'
|
||||
],
|
||||
'0'
|
||||
)
|
||||
t.assert.deepStrictEqual(
|
||||
thirdRateLimitWithResettingTheRateLimitTimer.headers['x-ratelimit-reset'],
|
||||
'5'
|
||||
)
|
||||
|
||||
t.assert.deepStrictEqual(okResponseAfterRateLimitCompleted.statusCode, 200)
|
||||
clock.reset(0)
|
||||
})
|
||||
87
backend/node_modules/@fastify/rate-limit/test/github-issues/issue-215.test.js
generated
vendored
Normal file
87
backend/node_modules/@fastify/rate-limit/test/github-issues/issue-215.test.js
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
'use strict'
|
||||
|
||||
const { test, mock } = require('node:test')
|
||||
const Fastify = require('fastify')
|
||||
const rateLimit = require('../../index')
|
||||
|
||||
test('issue #215 - when using local store, 2nd user should not be rate limited when the time window is passed for the 1st user', async (t) => {
|
||||
t.plan(5)
|
||||
const clock = mock.timers
|
||||
clock.enable()
|
||||
const fastify = Fastify()
|
||||
|
||||
await fastify.register(rateLimit, {
|
||||
global: false
|
||||
})
|
||||
|
||||
fastify.get(
|
||||
'/',
|
||||
{
|
||||
config: {
|
||||
rateLimit: {
|
||||
max: 1,
|
||||
timeWindow: 5000,
|
||||
continueExceeding: false
|
||||
}
|
||||
}
|
||||
},
|
||||
async () => 'hello!'
|
||||
)
|
||||
|
||||
const user1FirstRequest = await fastify.inject({
|
||||
url: '/',
|
||||
method: 'GET',
|
||||
remoteAddress: '1.1.1.1'
|
||||
})
|
||||
|
||||
// Waiting for the time to pass to make the 2nd user start in a different start point
|
||||
clock.tick(3000)
|
||||
|
||||
const user2FirstRequest = await fastify.inject({
|
||||
url: '/',
|
||||
method: 'GET',
|
||||
remoteAddress: '2.2.2.2'
|
||||
})
|
||||
|
||||
const user2SecondRequestAndShouldBeRateLimited = await fastify.inject({
|
||||
url: '/',
|
||||
method: 'GET',
|
||||
remoteAddress: '2.2.2.2'
|
||||
})
|
||||
|
||||
// After this the total time passed for the 1st user is 6s and for the 2nd user only 3s
|
||||
clock.tick(3000)
|
||||
|
||||
const user2ThirdRequestAndShouldStillBeRateLimited = await fastify.inject({
|
||||
url: '/',
|
||||
method: 'GET',
|
||||
remoteAddress: '2.2.2.2'
|
||||
})
|
||||
|
||||
// After this the total time passed for the 2nd user is 5.1s - he should not be rate limited
|
||||
clock.tick(2100)
|
||||
|
||||
const user2OkResponseAfterRateLimitCompleted = await fastify.inject({
|
||||
url: '/',
|
||||
method: 'GET',
|
||||
remoteAddress: '2.2.2.2'
|
||||
})
|
||||
|
||||
t.assert.deepStrictEqual(user1FirstRequest.statusCode, 200)
|
||||
t.assert.deepStrictEqual(user2FirstRequest.statusCode, 200)
|
||||
|
||||
t.assert.deepStrictEqual(
|
||||
user2SecondRequestAndShouldBeRateLimited.statusCode,
|
||||
429
|
||||
)
|
||||
t.assert.deepStrictEqual(
|
||||
user2ThirdRequestAndShouldStillBeRateLimited.statusCode,
|
||||
429
|
||||
)
|
||||
|
||||
t.assert.deepStrictEqual(
|
||||
user2OkResponseAfterRateLimitCompleted.statusCode,
|
||||
200
|
||||
)
|
||||
clock.reset()
|
||||
})
|
||||
74
backend/node_modules/@fastify/rate-limit/test/github-issues/issue-284.test.js
generated
vendored
Normal file
74
backend/node_modules/@fastify/rate-limit/test/github-issues/issue-284.test.js
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
'use strict'
|
||||
|
||||
const { test, mock } = require('node:test')
|
||||
const Fastify = require('fastify')
|
||||
const rateLimit = require('../../index')
|
||||
|
||||
test("issue #284 - don't set the reply code automatically", async (t) => {
|
||||
const clock = mock.timers
|
||||
clock.enable()
|
||||
const fastify = Fastify()
|
||||
|
||||
await fastify.register(rateLimit, {
|
||||
global: false
|
||||
})
|
||||
|
||||
fastify.setErrorHandler((err, _req, res) => {
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(err.statusCode, 429)
|
||||
|
||||
res.redirect('/')
|
||||
})
|
||||
|
||||
fastify.get(
|
||||
'/',
|
||||
{
|
||||
config: {
|
||||
rateLimit: {
|
||||
max: 1,
|
||||
timeWindow: 5000,
|
||||
continueExceeding: true
|
||||
}
|
||||
}
|
||||
},
|
||||
async () => {
|
||||
return 'hello!'
|
||||
}
|
||||
)
|
||||
|
||||
const firstOkResponse = await fastify.inject({
|
||||
url: '/',
|
||||
method: 'GET'
|
||||
})
|
||||
const firstRateLimitResponse = await fastify.inject({
|
||||
url: '/',
|
||||
method: 'GET'
|
||||
})
|
||||
|
||||
// After this the rate limiter should allow for new requests
|
||||
clock.tick(5000)
|
||||
|
||||
const okResponseAfterRateLimitCompleted = await fastify.inject({
|
||||
url: '/',
|
||||
method: 'GET'
|
||||
})
|
||||
|
||||
t.assert.deepStrictEqual(firstOkResponse.statusCode, 200)
|
||||
|
||||
t.assert.deepStrictEqual(firstRateLimitResponse.statusCode, 302)
|
||||
t.assert.deepStrictEqual(
|
||||
firstRateLimitResponse.headers['x-ratelimit-limit'],
|
||||
'1'
|
||||
)
|
||||
t.assert.deepStrictEqual(
|
||||
firstRateLimitResponse.headers['x-ratelimit-remaining'],
|
||||
'0'
|
||||
)
|
||||
t.assert.deepStrictEqual(
|
||||
firstRateLimitResponse.headers['x-ratelimit-reset'],
|
||||
'5'
|
||||
)
|
||||
|
||||
t.assert.deepStrictEqual(okResponseAfterRateLimitCompleted.statusCode, 200)
|
||||
clock.reset(0)
|
||||
})
|
||||
1451
backend/node_modules/@fastify/rate-limit/test/global-rate-limit.test.js
generated
vendored
Normal file
1451
backend/node_modules/@fastify/rate-limit/test/global-rate-limit.test.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
252
backend/node_modules/@fastify/rate-limit/test/group-rate-limit.test.js
generated
vendored
Normal file
252
backend/node_modules/@fastify/rate-limit/test/group-rate-limit.test.js
generated
vendored
Normal file
@@ -0,0 +1,252 @@
|
||||
'use strict'
|
||||
const { test } = require('node:test')
|
||||
const assert = require('node:assert')
|
||||
const Fastify = require('fastify')
|
||||
const rateLimit = require('../index')
|
||||
|
||||
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
||||
|
||||
test('GroupId from routeConfig', async () => {
|
||||
const fastify = Fastify()
|
||||
|
||||
// Register rate limit plugin with groupId in routeConfig
|
||||
await fastify.register(rateLimit, { max: 2, timeWindow: 500 })
|
||||
|
||||
fastify.get(
|
||||
'/routeWithGroupId',
|
||||
{
|
||||
config: {
|
||||
rateLimit: {
|
||||
max: 2,
|
||||
timeWindow: 500,
|
||||
groupId: 'group1' // groupId specified in routeConfig
|
||||
}
|
||||
}
|
||||
},
|
||||
async () => 'hello from route with groupId!'
|
||||
)
|
||||
|
||||
// Test: Request should have the correct groupId in response
|
||||
const res = await fastify.inject({ url: '/routeWithGroupId', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 200)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1')
|
||||
})
|
||||
|
||||
test('GroupId from routeOptions', async () => {
|
||||
const fastify = Fastify()
|
||||
|
||||
// Register rate limit plugin with groupId in routeOptions
|
||||
await fastify.register(rateLimit, { max: 2, timeWindow: 500 })
|
||||
|
||||
fastify.get(
|
||||
'/routeWithGroupIdFromOptions',
|
||||
{
|
||||
config: {
|
||||
rateLimit: {
|
||||
max: 2,
|
||||
timeWindow: 500
|
||||
// groupId not specified here
|
||||
}
|
||||
}
|
||||
},
|
||||
async () => 'hello from route with groupId from options!'
|
||||
)
|
||||
|
||||
// Test: Request should have the correct groupId from routeOptions
|
||||
const res = await fastify.inject({ url: '/routeWithGroupIdFromOptions', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 200)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1')
|
||||
})
|
||||
|
||||
test('No groupId provided', async () => {
|
||||
const fastify = Fastify()
|
||||
|
||||
// Register rate limit plugin without groupId
|
||||
await fastify.register(rateLimit, { max: 2, timeWindow: 500 })
|
||||
|
||||
// Route without groupId
|
||||
fastify.get(
|
||||
'/noGroupId',
|
||||
{
|
||||
config: {
|
||||
rateLimit: {
|
||||
max: 2,
|
||||
timeWindow: 500
|
||||
}
|
||||
}
|
||||
},
|
||||
async () => 'hello from no groupId route!'
|
||||
)
|
||||
|
||||
let res
|
||||
|
||||
// Test without groupId
|
||||
res = await fastify.inject({ url: '/noGroupId', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 200)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1')
|
||||
|
||||
res = await fastify.inject({ url: '/noGroupId', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 200)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
|
||||
res = await fastify.inject({ url: '/noGroupId', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 429)
|
||||
assert.deepStrictEqual(
|
||||
res.headers['content-type'],
|
||||
'application/json; charset=utf-8'
|
||||
)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
assert.deepStrictEqual(res.headers['retry-after'], '1')
|
||||
assert.deepStrictEqual(
|
||||
{
|
||||
statusCode: 429,
|
||||
error: 'Too Many Requests',
|
||||
message: 'Rate limit exceeded, retry in 1 second'
|
||||
},
|
||||
JSON.parse(res.payload)
|
||||
)
|
||||
})
|
||||
|
||||
test('With multiple routes and custom groupId', async () => {
|
||||
const fastify = Fastify()
|
||||
|
||||
// Register rate limit plugin
|
||||
await fastify.register(rateLimit, { max: 2, timeWindow: 500 })
|
||||
|
||||
// Route 1 with groupId 'group1'
|
||||
fastify.get(
|
||||
'/route1',
|
||||
{
|
||||
config: {
|
||||
rateLimit: {
|
||||
max: 2,
|
||||
timeWindow: 500,
|
||||
groupId: 'group1'
|
||||
}
|
||||
}
|
||||
},
|
||||
async () => 'hello from route 1!'
|
||||
)
|
||||
|
||||
// Route 2 with groupId 'group2'
|
||||
fastify.get(
|
||||
'/route2',
|
||||
{
|
||||
config: {
|
||||
rateLimit: {
|
||||
max: 2,
|
||||
timeWindow: 1000,
|
||||
groupId: 'group2'
|
||||
}
|
||||
}
|
||||
},
|
||||
async () => 'hello from route 2!'
|
||||
)
|
||||
|
||||
let res
|
||||
|
||||
// Test Route 1
|
||||
res = await fastify.inject({ url: '/route1', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 200)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1')
|
||||
|
||||
res = await fastify.inject({ url: '/route1', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 200)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
|
||||
res = await fastify.inject({ url: '/route1', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 429)
|
||||
assert.deepStrictEqual(
|
||||
res.headers['content-type'],
|
||||
'application/json; charset=utf-8'
|
||||
)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
assert.deepStrictEqual(res.headers['retry-after'], '1')
|
||||
assert.deepStrictEqual(
|
||||
{
|
||||
statusCode: 429,
|
||||
error: 'Too Many Requests',
|
||||
message: 'Rate limit exceeded, retry in 1 second'
|
||||
},
|
||||
JSON.parse(res.payload)
|
||||
)
|
||||
|
||||
// Test Route 2
|
||||
res = await fastify.inject({ url: '/route2', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 200)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1')
|
||||
|
||||
res = await fastify.inject({ url: '/route2', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 200)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
|
||||
res = await fastify.inject({ url: '/route2', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 429)
|
||||
assert.deepStrictEqual(
|
||||
res.headers['content-type'],
|
||||
'application/json; charset=utf-8'
|
||||
)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
assert.deepStrictEqual(res.headers['retry-after'], '1')
|
||||
assert.deepStrictEqual(
|
||||
{
|
||||
statusCode: 429,
|
||||
error: 'Too Many Requests',
|
||||
message: 'Rate limit exceeded, retry in 1 second'
|
||||
},
|
||||
JSON.parse(res.payload)
|
||||
)
|
||||
|
||||
// Wait for the window to reset
|
||||
await sleep(1000)
|
||||
|
||||
// After reset, Route 1 should succeed again
|
||||
res = await fastify.inject({ url: '/route1', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 200)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1')
|
||||
|
||||
// Route 2 should also succeed after the reset
|
||||
res = await fastify.inject({ url: '/route2', method: 'GET' })
|
||||
assert.deepStrictEqual(res.statusCode, 200)
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1')
|
||||
})
|
||||
|
||||
test('Invalid groupId type', async () => {
|
||||
const fastify = Fastify()
|
||||
|
||||
// Register rate limit plugin with a route having an invalid groupId
|
||||
await fastify.register(rateLimit, { max: 2, timeWindow: 1000 })
|
||||
|
||||
try {
|
||||
fastify.get(
|
||||
'/invalidGroupId',
|
||||
{
|
||||
config: {
|
||||
rateLimit: {
|
||||
max: 2,
|
||||
timeWindow: 1000,
|
||||
groupId: 123 // Invalid groupId type
|
||||
}
|
||||
}
|
||||
},
|
||||
async () => 'hello with invalid groupId!'
|
||||
)
|
||||
assert.fail('should throw')
|
||||
console.log('HER')
|
||||
} catch (err) {
|
||||
assert.deepStrictEqual(err.message, 'groupId must be a string')
|
||||
}
|
||||
})
|
||||
18
backend/node_modules/@fastify/rate-limit/test/local-store-close.test.js
generated
vendored
Normal file
18
backend/node_modules/@fastify/rate-limit/test/local-store-close.test.js
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
'use strict'
|
||||
|
||||
const { test } = require('node:test')
|
||||
const Fastify = require('fastify')
|
||||
const rateLimit = require('../index')
|
||||
|
||||
test('Fastify close on local store', async (t) => {
|
||||
t.plan(1)
|
||||
const fastify = Fastify()
|
||||
await fastify.register(rateLimit, { max: 2, timeWindow: 1000 })
|
||||
let counter = 1
|
||||
fastify.addHook('onClose', (_instance, done) => {
|
||||
counter++
|
||||
done()
|
||||
})
|
||||
await fastify.close()
|
||||
t.assert.deepStrictEqual(counter, 2)
|
||||
})
|
||||
116
backend/node_modules/@fastify/rate-limit/test/not-found-handler-rate-limited.test.js
generated
vendored
Normal file
116
backend/node_modules/@fastify/rate-limit/test/not-found-handler-rate-limited.test.js
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
'use strict'
|
||||
|
||||
const { test } = require('node:test')
|
||||
const Fastify = require('fastify')
|
||||
const rateLimit = require('../index')
|
||||
|
||||
test('Set not found handler can be rate limited', async (t) => {
|
||||
t.plan(18)
|
||||
|
||||
const fastify = Fastify()
|
||||
|
||||
await fastify.register(rateLimit, { max: 2, timeWindow: 1000 })
|
||||
t.assert.ok(fastify.rateLimit)
|
||||
|
||||
fastify.setNotFoundHandler(
|
||||
{
|
||||
preHandler: fastify.rateLimit()
|
||||
},
|
||||
function (_request, reply) {
|
||||
t.assert.ok('Error handler has been called')
|
||||
reply.status(404).send(new Error('Not found'))
|
||||
}
|
||||
)
|
||||
|
||||
let res
|
||||
res = await fastify.inject('/not-found')
|
||||
t.assert.deepStrictEqual(res.statusCode, 404)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '1')
|
||||
|
||||
res = await fastify.inject('/not-found')
|
||||
t.assert.deepStrictEqual(res.statusCode, 404)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '1')
|
||||
|
||||
res = await fastify.inject('/not-found')
|
||||
t.assert.deepStrictEqual(res.statusCode, 429)
|
||||
t.assert.deepStrictEqual(
|
||||
res.headers['content-type'],
|
||||
'application/json; charset=utf-8'
|
||||
)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '1')
|
||||
t.assert.deepStrictEqual(res.headers['retry-after'], '1')
|
||||
t.assert.deepStrictEqual(JSON.parse(res.payload), {
|
||||
statusCode: 429,
|
||||
error: 'Too Many Requests',
|
||||
message: 'Rate limit exceeded, retry in 1 second'
|
||||
})
|
||||
})
|
||||
|
||||
test('Set not found handler can be rate limited with specific options', async (t) => {
|
||||
t.plan(28)
|
||||
|
||||
const fastify = Fastify()
|
||||
|
||||
await fastify.register(rateLimit, { max: 2, timeWindow: 1000 })
|
||||
t.assert.ok(fastify.rateLimit)
|
||||
|
||||
fastify.setNotFoundHandler(
|
||||
{
|
||||
preHandler: fastify.rateLimit({
|
||||
max: 4,
|
||||
timeWindow: 2000
|
||||
})
|
||||
},
|
||||
function (_request, reply) {
|
||||
t.assert.ok('Error handler has been called')
|
||||
reply.status(404).send(new Error('Not found'))
|
||||
}
|
||||
)
|
||||
|
||||
let res
|
||||
res = await fastify.inject('/not-found')
|
||||
t.assert.deepStrictEqual(res.statusCode, 404)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '4')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '3')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '2')
|
||||
|
||||
res = await fastify.inject('/not-found')
|
||||
t.assert.deepStrictEqual(res.statusCode, 404)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '4')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '2')
|
||||
|
||||
res = await fastify.inject('/not-found')
|
||||
t.assert.deepStrictEqual(res.statusCode, 404)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '4')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '2')
|
||||
|
||||
res = await fastify.inject('/not-found')
|
||||
t.assert.deepStrictEqual(res.statusCode, 404)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '4')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '2')
|
||||
|
||||
res = await fastify.inject('/not-found')
|
||||
t.assert.deepStrictEqual(res.statusCode, 429)
|
||||
t.assert.deepStrictEqual(
|
||||
res.headers['content-type'],
|
||||
'application/json; charset=utf-8'
|
||||
)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '4')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(res.headers['retry-after'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '2')
|
||||
t.assert.deepStrictEqual(JSON.parse(res.payload), {
|
||||
statusCode: 429,
|
||||
error: 'Too Many Requests',
|
||||
message: 'Rate limit exceeded, retry in 2 seconds'
|
||||
})
|
||||
})
|
||||
753
backend/node_modules/@fastify/rate-limit/test/redis-rate-limit.test.js
generated
vendored
Normal file
753
backend/node_modules/@fastify/rate-limit/test/redis-rate-limit.test.js
generated
vendored
Normal file
@@ -0,0 +1,753 @@
|
||||
'use strict'
|
||||
|
||||
const { test, describe } = require('node:test')
|
||||
const Redis = require('ioredis')
|
||||
const Fastify = require('fastify')
|
||||
const rateLimit = require('../index')
|
||||
|
||||
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
||||
|
||||
const REDIS_HOST = '127.0.0.1'
|
||||
|
||||
describe('Global rate limit', () => {
|
||||
test('With redis store', async (t) => {
|
||||
t.plan(21)
|
||||
const fastify = Fastify()
|
||||
const redis = await new Redis({ host: REDIS_HOST })
|
||||
await fastify.register(rateLimit, {
|
||||
max: 2,
|
||||
timeWindow: 1000,
|
||||
redis
|
||||
})
|
||||
|
||||
fastify.get('/', async () => 'hello!')
|
||||
|
||||
let res
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.strictEqual(res.statusCode, 200)
|
||||
t.assert.ok(res)
|
||||
t.assert.strictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '1')
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '1')
|
||||
|
||||
await sleep(100)
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 429)
|
||||
t.assert.deepStrictEqual(
|
||||
res.headers['content-type'],
|
||||
'application/json; charset=utf-8'
|
||||
)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '1')
|
||||
t.assert.deepStrictEqual(res.headers['retry-after'], '1')
|
||||
t.assert.deepStrictEqual(
|
||||
{
|
||||
statusCode: 429,
|
||||
error: 'Too Many Requests',
|
||||
message: 'Rate limit exceeded, retry in 1 second'
|
||||
},
|
||||
JSON.parse(res.payload)
|
||||
)
|
||||
|
||||
// Not using fake timers here as we use an external Redis that would not be effected by this
|
||||
await sleep(1100)
|
||||
|
||||
res = await fastify.inject('/')
|
||||
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '1')
|
||||
|
||||
await redis.flushall()
|
||||
await redis.quit()
|
||||
})
|
||||
|
||||
test('With redis store (ban)', async (t) => {
|
||||
t.plan(19)
|
||||
const fastify = Fastify()
|
||||
const redis = await new Redis({ host: REDIS_HOST })
|
||||
await fastify.register(rateLimit, {
|
||||
max: 1,
|
||||
ban: 1,
|
||||
timeWindow: 1000,
|
||||
redis
|
||||
})
|
||||
|
||||
fastify.get('/', async () => 'hello!')
|
||||
|
||||
let res
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '1')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '1')
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 429)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '1')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '1')
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 403)
|
||||
t.assert.deepStrictEqual(
|
||||
res.headers['content-type'],
|
||||
'application/json; charset=utf-8'
|
||||
)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '1')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '1')
|
||||
t.assert.deepStrictEqual(res.headers['retry-after'], '1')
|
||||
t.assert.deepStrictEqual(
|
||||
{
|
||||
statusCode: 403,
|
||||
error: 'Forbidden',
|
||||
message: 'Rate limit exceeded, retry in 1 second'
|
||||
},
|
||||
JSON.parse(res.payload)
|
||||
)
|
||||
|
||||
// Not using fake timers here as we use an external Redis that would not be effected by this
|
||||
await sleep(1100)
|
||||
|
||||
res = await fastify.inject('/')
|
||||
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '1')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '1')
|
||||
|
||||
await redis.flushall()
|
||||
await redis.quit()
|
||||
})
|
||||
|
||||
test('Skip on redis error', async (t) => {
|
||||
t.plan(9)
|
||||
const fastify = Fastify()
|
||||
const redis = await new Redis({ host: REDIS_HOST })
|
||||
await fastify.register(rateLimit, {
|
||||
max: 2,
|
||||
timeWindow: 1000,
|
||||
redis,
|
||||
skipOnError: true
|
||||
})
|
||||
|
||||
fastify.get('/', async () => 'hello!')
|
||||
|
||||
let res
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1')
|
||||
|
||||
await redis.flushall()
|
||||
await redis.quit()
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '2')
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '2')
|
||||
})
|
||||
|
||||
test('Throw on redis error', async (t) => {
|
||||
t.plan(5)
|
||||
const fastify = Fastify()
|
||||
const redis = await new Redis({ host: REDIS_HOST })
|
||||
await fastify.register(rateLimit, {
|
||||
max: 2,
|
||||
timeWindow: 1000,
|
||||
redis,
|
||||
skipOnError: false
|
||||
})
|
||||
|
||||
fastify.get('/', async () => 'hello!')
|
||||
|
||||
let res
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1')
|
||||
|
||||
await redis.flushall()
|
||||
await redis.quit()
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 500)
|
||||
t.assert.deepStrictEqual(
|
||||
res.body,
|
||||
'{"statusCode":500,"error":"Internal Server Error","message":"Connection is closed."}'
|
||||
)
|
||||
})
|
||||
|
||||
test('When continue exceeding is on (Redis)', async (t) => {
|
||||
const fastify = Fastify()
|
||||
const redis = await new Redis({ host: REDIS_HOST })
|
||||
|
||||
await fastify.register(rateLimit, {
|
||||
redis,
|
||||
max: 1,
|
||||
timeWindow: 5000,
|
||||
continueExceeding: true
|
||||
})
|
||||
|
||||
fastify.get('/', async () => 'hello!')
|
||||
|
||||
const first = await fastify.inject({
|
||||
url: '/',
|
||||
method: 'GET'
|
||||
})
|
||||
const second = await fastify.inject({
|
||||
url: '/',
|
||||
method: 'GET'
|
||||
})
|
||||
|
||||
t.assert.deepStrictEqual(first.statusCode, 200)
|
||||
|
||||
t.assert.deepStrictEqual(second.statusCode, 429)
|
||||
t.assert.deepStrictEqual(second.headers['x-ratelimit-limit'], '1')
|
||||
t.assert.deepStrictEqual(second.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(second.headers['x-ratelimit-reset'], '5')
|
||||
|
||||
await redis.flushall()
|
||||
await redis.quit()
|
||||
})
|
||||
|
||||
test('Redis with continueExceeding should not always return the timeWindow as ttl', async (t) => {
|
||||
t.plan(19)
|
||||
const fastify = Fastify()
|
||||
const redis = await new Redis({ host: REDIS_HOST })
|
||||
await fastify.register(rateLimit, {
|
||||
max: 2,
|
||||
timeWindow: 3000,
|
||||
continueExceeding: true,
|
||||
redis
|
||||
})
|
||||
|
||||
fastify.get('/', async () => 'hello!')
|
||||
|
||||
let res
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '3')
|
||||
|
||||
// After this sleep, we should not see `x-ratelimit-reset === 3` anymore
|
||||
await sleep(1000)
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '2')
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 429)
|
||||
t.assert.deepStrictEqual(
|
||||
res.headers['content-type'],
|
||||
'application/json; charset=utf-8'
|
||||
)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '3')
|
||||
t.assert.deepStrictEqual(res.headers['retry-after'], '3')
|
||||
t.assert.deepStrictEqual(
|
||||
{
|
||||
statusCode: 429,
|
||||
error: 'Too Many Requests',
|
||||
message: 'Rate limit exceeded, retry in 3 seconds'
|
||||
},
|
||||
JSON.parse(res.payload)
|
||||
)
|
||||
|
||||
// Not using fake timers here as we use an external Redis that would not be effected by this
|
||||
await sleep(1000)
|
||||
|
||||
res = await fastify.inject('/')
|
||||
|
||||
t.assert.deepStrictEqual(res.statusCode, 429)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '3')
|
||||
|
||||
await redis.flushall()
|
||||
await redis.quit()
|
||||
})
|
||||
|
||||
test('When use a custom nameSpace', async (t) => {
|
||||
const fastify = Fastify()
|
||||
const redis = await new Redis({ host: REDIS_HOST })
|
||||
|
||||
await fastify.register(rateLimit, {
|
||||
max: 2,
|
||||
timeWindow: 1000,
|
||||
redis,
|
||||
nameSpace: 'my-namespace:',
|
||||
keyGenerator: (req) => req.headers['x-my-header']
|
||||
})
|
||||
|
||||
fastify.get('/', async () => 'hello!')
|
||||
|
||||
const allowListHeader = {
|
||||
method: 'GET',
|
||||
url: '/',
|
||||
headers: {
|
||||
'x-my-header': 'custom name space'
|
||||
}
|
||||
}
|
||||
|
||||
let res
|
||||
|
||||
res = await fastify.inject(allowListHeader)
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '1')
|
||||
|
||||
res = await fastify.inject(allowListHeader)
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '1')
|
||||
|
||||
res = await fastify.inject(allowListHeader)
|
||||
t.assert.deepStrictEqual(res.statusCode, 429)
|
||||
t.assert.deepStrictEqual(
|
||||
res.headers['content-type'],
|
||||
'application/json; charset=utf-8'
|
||||
)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '1')
|
||||
t.assert.deepStrictEqual(res.headers['retry-after'], '1')
|
||||
t.assert.deepStrictEqual(
|
||||
{
|
||||
statusCode: 429,
|
||||
error: 'Too Many Requests',
|
||||
message: 'Rate limit exceeded, retry in 1 second'
|
||||
},
|
||||
JSON.parse(res.payload)
|
||||
)
|
||||
|
||||
// Not using fake timers here as we use an external Redis that would not be effected by this
|
||||
await sleep(1100)
|
||||
|
||||
res = await fastify.inject(allowListHeader)
|
||||
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '1')
|
||||
|
||||
await redis.flushall()
|
||||
await redis.quit()
|
||||
})
|
||||
|
||||
test('With redis store and exponential backoff', async (t) => {
|
||||
t.plan(20)
|
||||
const fastify = Fastify()
|
||||
const redis = await new Redis({ host: REDIS_HOST })
|
||||
await fastify.register(rateLimit, {
|
||||
max: 2,
|
||||
timeWindow: 1000,
|
||||
redis,
|
||||
exponentialBackoff: true
|
||||
})
|
||||
|
||||
fastify.get('/', async () => 'hello!')
|
||||
|
||||
let res
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '1')
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '1')
|
||||
|
||||
// First attempt over the limit should have the normal timeWindow (1000ms)
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 429)
|
||||
t.assert.deepStrictEqual(
|
||||
res.headers['content-type'],
|
||||
'application/json; charset=utf-8'
|
||||
)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '1')
|
||||
t.assert.deepStrictEqual(res.headers['retry-after'], '1')
|
||||
t.assert.deepStrictEqual(
|
||||
{
|
||||
statusCode: 429,
|
||||
error: 'Too Many Requests',
|
||||
message: 'Rate limit exceeded, retry in 1 second'
|
||||
},
|
||||
JSON.parse(res.payload)
|
||||
)
|
||||
|
||||
// Second attempt over the limit should have doubled timeWindow (2000ms)
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 429)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(res.headers['retry-after'], '2')
|
||||
t.assert.deepStrictEqual(
|
||||
{
|
||||
statusCode: 429,
|
||||
error: 'Too Many Requests',
|
||||
message: 'Rate limit exceeded, retry in 2 seconds'
|
||||
},
|
||||
JSON.parse(res.payload)
|
||||
)
|
||||
|
||||
await redis.flushall()
|
||||
await redis.quit()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Route rate limit', () => {
|
||||
test('With redis store', async t => {
|
||||
t.plan(19)
|
||||
const fastify = Fastify()
|
||||
const redis = new Redis({ host: REDIS_HOST })
|
||||
await fastify.register(rateLimit, {
|
||||
global: false,
|
||||
redis
|
||||
})
|
||||
|
||||
fastify.get('/', {
|
||||
config: {
|
||||
rateLimit: {
|
||||
max: 2,
|
||||
timeWindow: 1000
|
||||
},
|
||||
someOtherPlugin: {
|
||||
someValue: 1
|
||||
}
|
||||
}
|
||||
}, async () => 'hello!')
|
||||
|
||||
let res
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.strictEqual(res.statusCode, 200)
|
||||
t.assert.strictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.strictEqual(res.headers['x-ratelimit-remaining'], '1')
|
||||
t.assert.strictEqual(res.headers['x-ratelimit-reset'], '1')
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.strictEqual(res.statusCode, 200)
|
||||
t.assert.strictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.strictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.strictEqual(res.headers['x-ratelimit-reset'], '1')
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.strictEqual(res.statusCode, 429)
|
||||
t.assert.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8')
|
||||
t.assert.strictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.strictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.strictEqual(res.headers['x-ratelimit-reset'], '1')
|
||||
t.assert.strictEqual(res.headers['retry-after'], '1')
|
||||
t.assert.deepStrictEqual({
|
||||
statusCode: 429,
|
||||
error: 'Too Many Requests',
|
||||
message: 'Rate limit exceeded, retry in 1 second'
|
||||
}, JSON.parse(res.payload))
|
||||
|
||||
// Not using fake timers here as we use an external Redis that would not be effected by this
|
||||
await sleep(1100)
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.strictEqual(res.statusCode, 200)
|
||||
t.assert.strictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.strictEqual(res.headers['x-ratelimit-remaining'], '1')
|
||||
t.assert.strictEqual(res.headers['x-ratelimit-reset'], '1')
|
||||
|
||||
await redis.flushall()
|
||||
await redis.quit()
|
||||
})
|
||||
|
||||
test('Throw on redis error', async (t) => {
|
||||
t.plan(6)
|
||||
const fastify = Fastify()
|
||||
const redis = new Redis({ host: REDIS_HOST })
|
||||
await fastify.register(rateLimit, {
|
||||
redis,
|
||||
global: false
|
||||
})
|
||||
|
||||
fastify.get(
|
||||
'/',
|
||||
{
|
||||
config: {
|
||||
rateLimit: {
|
||||
max: 2,
|
||||
timeWindow: 1000,
|
||||
skipOnError: false
|
||||
}
|
||||
}
|
||||
},
|
||||
async () => 'hello!'
|
||||
)
|
||||
|
||||
let res
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '1')
|
||||
|
||||
await redis.flushall()
|
||||
await redis.quit()
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 500)
|
||||
t.assert.deepStrictEqual(
|
||||
res.body,
|
||||
'{"statusCode":500,"error":"Internal Server Error","message":"Connection is closed."}'
|
||||
)
|
||||
})
|
||||
|
||||
test('Skip on redis error', async (t) => {
|
||||
t.plan(9)
|
||||
const fastify = Fastify()
|
||||
const redis = new Redis({ host: REDIS_HOST })
|
||||
await fastify.register(rateLimit, {
|
||||
redis,
|
||||
global: false
|
||||
})
|
||||
|
||||
fastify.get(
|
||||
'/',
|
||||
{
|
||||
config: {
|
||||
rateLimit: {
|
||||
max: 2,
|
||||
timeWindow: 1000,
|
||||
skipOnError: true
|
||||
}
|
||||
}
|
||||
},
|
||||
async () => 'hello!'
|
||||
)
|
||||
|
||||
let res
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1')
|
||||
|
||||
await redis.flushall()
|
||||
await redis.quit()
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '2')
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '2')
|
||||
})
|
||||
|
||||
test('When continue exceeding is on (Redis)', async (t) => {
|
||||
const fastify = Fastify()
|
||||
const redis = await new Redis({ host: REDIS_HOST })
|
||||
|
||||
await fastify.register(rateLimit, {
|
||||
global: false,
|
||||
redis
|
||||
})
|
||||
|
||||
fastify.get(
|
||||
'/',
|
||||
{
|
||||
config: {
|
||||
rateLimit: {
|
||||
timeWindow: 5000,
|
||||
max: 1,
|
||||
continueExceeding: true
|
||||
}
|
||||
}
|
||||
},
|
||||
async () => 'hello!'
|
||||
)
|
||||
|
||||
const first = await fastify.inject({
|
||||
url: '/',
|
||||
method: 'GET'
|
||||
})
|
||||
const second = await fastify.inject({
|
||||
url: '/',
|
||||
method: 'GET'
|
||||
})
|
||||
|
||||
t.assert.deepStrictEqual(first.statusCode, 200)
|
||||
|
||||
t.assert.deepStrictEqual(second.statusCode, 429)
|
||||
t.assert.deepStrictEqual(second.headers['x-ratelimit-limit'], '1')
|
||||
t.assert.deepStrictEqual(second.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(second.headers['x-ratelimit-reset'], '5')
|
||||
|
||||
await redis.flushall()
|
||||
await redis.quit()
|
||||
})
|
||||
|
||||
test('When continue exceeding is off under route (Redis)', async (t) => {
|
||||
const fastify = Fastify()
|
||||
const redis = await new Redis({ host: REDIS_HOST })
|
||||
|
||||
await fastify.register(rateLimit, {
|
||||
global: false,
|
||||
continueExceeding: true,
|
||||
redis
|
||||
})
|
||||
|
||||
fastify.get(
|
||||
'/',
|
||||
{
|
||||
config: {
|
||||
rateLimit: {
|
||||
timeWindow: 5000,
|
||||
max: 1,
|
||||
continueExceeding: false
|
||||
}
|
||||
}
|
||||
},
|
||||
async () => 'hello!'
|
||||
)
|
||||
|
||||
const first = await fastify.inject({
|
||||
url: '/',
|
||||
method: 'GET'
|
||||
})
|
||||
const second = await fastify.inject({
|
||||
url: '/',
|
||||
method: 'GET'
|
||||
})
|
||||
|
||||
await sleep(2000)
|
||||
|
||||
const third = await fastify.inject({
|
||||
url: '/',
|
||||
method: 'GET'
|
||||
})
|
||||
|
||||
t.assert.deepStrictEqual(first.statusCode, 200)
|
||||
|
||||
t.assert.deepStrictEqual(second.statusCode, 429)
|
||||
t.assert.deepStrictEqual(second.headers['x-ratelimit-limit'], '1')
|
||||
t.assert.deepStrictEqual(second.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(second.headers['x-ratelimit-reset'], '5')
|
||||
|
||||
t.assert.deepStrictEqual(third.statusCode, 429)
|
||||
t.assert.deepStrictEqual(third.headers['x-ratelimit-limit'], '1')
|
||||
t.assert.deepStrictEqual(third.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(third.headers['x-ratelimit-reset'], '3')
|
||||
|
||||
await redis.flushall()
|
||||
await redis.quit()
|
||||
})
|
||||
|
||||
test('Route-specific exponential backoff with redis store', async (t) => {
|
||||
t.plan(17)
|
||||
const fastify = Fastify()
|
||||
const redis = await new Redis({ host: REDIS_HOST })
|
||||
await fastify.register(rateLimit, {
|
||||
global: false,
|
||||
redis
|
||||
})
|
||||
|
||||
fastify.get('/', {
|
||||
config: {
|
||||
rateLimit: {
|
||||
max: 1,
|
||||
timeWindow: 1000,
|
||||
exponentialBackoff: true
|
||||
}
|
||||
}
|
||||
}, async () => 'hello!')
|
||||
|
||||
let res
|
||||
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 200)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '1')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-reset'], '1')
|
||||
|
||||
// First attempt over the limit should have the normal timeWindow (1000ms)
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 429)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '1')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(res.headers['retry-after'], '1')
|
||||
t.assert.deepStrictEqual(
|
||||
{
|
||||
statusCode: 429,
|
||||
error: 'Too Many Requests',
|
||||
message: 'Rate limit exceeded, retry in 1 second'
|
||||
},
|
||||
JSON.parse(res.payload)
|
||||
)
|
||||
|
||||
// Second attempt over the limit should have doubled timeWindow (2000ms)
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 429)
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '1')
|
||||
t.assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0')
|
||||
t.assert.deepStrictEqual(res.headers['retry-after'], '2')
|
||||
t.assert.deepStrictEqual(
|
||||
{
|
||||
statusCode: 429,
|
||||
error: 'Too Many Requests',
|
||||
message: 'Rate limit exceeded, retry in 2 seconds'
|
||||
},
|
||||
JSON.parse(res.payload)
|
||||
)
|
||||
|
||||
// Third attempt over the limit should have quadrupled timeWindow (4000ms)
|
||||
res = await fastify.inject('/')
|
||||
t.assert.deepStrictEqual(res.statusCode, 429)
|
||||
t.assert.deepStrictEqual(res.headers['retry-after'], '4')
|
||||
t.assert.deepStrictEqual(
|
||||
{
|
||||
statusCode: 429,
|
||||
error: 'Too Many Requests',
|
||||
message: 'Rate limit exceeded, retry in 4 seconds'
|
||||
},
|
||||
JSON.parse(res.payload)
|
||||
)
|
||||
|
||||
await redis.flushall()
|
||||
await redis.quit()
|
||||
})
|
||||
})
|
||||
1769
backend/node_modules/@fastify/rate-limit/test/route-rate-limit.test.js
generated
vendored
Normal file
1769
backend/node_modules/@fastify/rate-limit/test/route-rate-limit.test.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user