Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

fastify-rate-limit

Package Overview
Dependencies
Maintainers
13
Versions
36
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fastify-rate-limit - npm Package Compare versions

Comparing version 5.0.1 to 5.1.0

12

index.d.ts

@@ -31,3 +31,3 @@ /// <reference types="node" />

interface AddHeaders {
interface DefaultAddHeaders {
'x-ratelimit-limit'?: boolean,

@@ -39,2 +39,9 @@ 'x-ratelimit-remaining'?: boolean,

interface DraftSpecAddHeaders {
'ratelimit-limit'?: boolean,
'ratelimit-remaining'?: boolean,
'ratelimit-reset'?: boolean,
'retry-after'?: boolean
}
export interface RateLimitPluginOptions {

@@ -56,3 +63,4 @@ global?: boolean;

errorResponseBuilder?: (req: FastifyRequest, context: errorResponseBuilderContext) => object;
addHeaders?: AddHeaders;
addHeaders?: DefaultAddHeaders | DraftSpecAddHeaders;
enableDraftSpec?: boolean;
}

@@ -59,0 +67,0 @@

48

index.js

@@ -11,2 +11,16 @@ 'use strict'

let labels = {
rateLimit: 'x-ratelimit-limit',
rateRemaining: 'x-ratelimit-remaining',
rateReset: 'x-ratelimit-reset',
retryAfter: 'retry-after'
}
const draftSpecHeaders = {
rateLimit: 'ratelimit-limit',
rateRemaining: 'ratelimit-remaining',
rateReset: 'ratelimit-reset',
retryAfter: 'retry-after'
}
function rateLimitPlugin (fastify, settings, next) {

@@ -19,9 +33,16 @@ // create the object that will hold the "main" settings that can be shared during the build

if (typeof settings.enableDraftSpec === 'boolean' && settings.enableDraftSpec) {
globalParams.enableDraftSpec = true
labels = draftSpecHeaders
}
globalParams.addHeaders = Object.assign({
'x-ratelimit-limit': true,
'x-ratelimit-remaining': true,
'x-ratelimit-reset': true,
'retry-after': true
[labels.rateLimit]: true,
[labels.rateRemaining]: true,
[labels.rateReset]: true,
[labels.retryAfter]: true
}, settings.addHeaders)
globalParams.labels = labels
// define the global maximum of request allowed

@@ -149,6 +170,8 @@ globalParams.max = (typeof settings.max === 'number' || typeof settings.max === 'function')

const maximum = getMax()
const timeLeft = Math.floor(ttl / 1000)
if (current <= maximum) {
res.header('x-ratelimit-limit', maximum)
.header('x-ratelimit-remaining', maximum - current)
.header('x-ratelimit-reset', Math.floor(ttl / 1000))
res.header(params.labels.rateLimit, maximum)
.header(params.labels.rateRemaining, maximum - current)
.header(params.labels.rateReset, timeLeft)

@@ -165,6 +188,9 @@ if (typeof params.onExceeding === 'function') {

if (params.addHeaders['x-ratelimit-limit']) { res.header('x-ratelimit-limit', maximum) }
if (params.addHeaders['x-ratelimit-remaining']) { res.header('x-ratelimit-remaining', 0) }
if (params.addHeaders['x-ratelimit-reset']) { res.header('x-ratelimit-reset', Math.floor(ttl / 1000)) }
if (params.addHeaders['retry-after']) { res.header('retry-after', params.timeWindow) }
if (params.addHeaders[params.labels.rateLimit]) { res.header(params.labels.rateLimit, maximum) }
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
res.header(params.labels.retryAfter, resetAfterTime)
}

@@ -171,0 +197,0 @@ const code = params.ban && current - maximum > params.ban ? 403 : 429

{
"name": "fastify-rate-limit",
"version": "5.0.1",
"version": "5.1.0",
"description": "A low overhead rate limiter for your routes",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -79,2 +79,3 @@ # fastify-rate-limit

errorResponseBuilder: function(req, context) { /* ... */},
enableDraftSpec: true, // default false. Uses IEFT draft header standard
addHeaders: { // default show all the response headers when rate limit is reached

@@ -102,2 +103,3 @@ 'x-ratelimit-limit': true,

- `addHeaders`: define which headers should be added in the response when the limit is reached. Defaults all the headers will be shown
- `enableDraftSpec`: if `true` it will change the HTTP rate limit headers following the IEFT draft document. More information at [draft-ietf-httpapi-ratelimit-headers.md](https://github.com/ietf-wg-httpapi/ratelimit-headers/blob/f6a7bc7560a776ea96d800cf5ed3752d6d397b06/draft-ietf-httpapi-ratelimit-headers.md).

@@ -278,2 +280,15 @@ `keyGenerator` example usage:

### IETF Draft Spec Headers
The response will have the following headers if `enableDraftSpec` is `true`:
| Header | Description |
|--------|-------------|
|`ratelimit-limit` | how many request the client can do
|`ratelimit-remaining` | how many request remain to the client in the timewindow
|`ratelimit-reset` | how many seconds must pass before the rate limit resets
|`retry-after` | contains the same value in time as `ratelimit-reset`
<a name="license"></a>

@@ -280,0 +295,0 @@ ## License

@@ -663,1 +663,137 @@ 'use strict'

})
test('With enabled IETF Draft Spec', t => {
t.plan(19)
const fastify = Fastify()
fastify.register(rateLimit, {
max: 2,
timeWindow: '1s',
enableDraftSpec: true
})
fastify.get('/', (req, reply) => {
reply.send('hello!')
})
fastify.inject('/', (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
t.strictEqual(res.headers['ratelimit-limit'], 2)
t.strictEqual(res.headers['ratelimit-remaining'], 1)
fastify.inject('/', (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
t.strictEqual(res.headers['ratelimit-limit'], 2)
t.strictEqual(res.headers['ratelimit-remaining'], 0)
fastify.inject('/', (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 429)
t.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8')
t.strictEqual(res.headers['ratelimit-limit'], 2)
t.strictEqual(res.headers['ratelimit-remaining'], 0)
t.strictEqual(res.headers['ratelimit-remaining'], res.headers['retry-after'])
t.deepEqual({
statusCode: 429,
error: 'Too Many Requests',
message: 'Rate limit exceeded, retry in 1 second'
}, JSON.parse(res.payload))
setTimeout(retry, 1100)
})
})
})
function retry () {
fastify.inject('/', (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
t.strictEqual(res.headers['ratelimit-limit'], 2)
t.strictEqual(res.headers['ratelimit-remaining'], 1)
})
}
})
test('hide IETF draft spec headers', t => {
t.plan(17)
const fastify = Fastify()
fastify.register(rateLimit, {
max: 1,
timeWindow: 1000,
enableDraftSpec: true,
addHeaders: {
'ratelimit-limit': false,
'ratelimit-remaining': false,
'ratelimit-reset': false,
'retry-after': false
}
})
fastify.get('/', (req, res) => { res.send('hello') })
fastify.inject('/', (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
t.strictEqual(res.headers['ratelimit-limit'], 1)
t.strictEqual(res.headers['ratelimit-remaining'], 0)
t.strictEqual(res.headers['ratelimit-reset'], 1)
fastify.inject('/', (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 429)
t.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8')
t.notOk(res.headers['ratelimit-limit'], 'the header must be missing')
t.notOk(res.headers['ratelimit-remaining'], 'the header must be missing')
t.notOk(res.headers['ratelimit-reset'], 'the header must be missing')
t.notOk(res.headers['retry-after'], 'the header must be missing')
setTimeout(retry, 1100)
})
})
function retry () {
fastify.inject('/', (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
t.strictEqual(res.headers['ratelimit-limit'], 1)
t.strictEqual(res.headers['ratelimit-remaining'], 0)
t.strictEqual(res.headers['ratelimit-reset'], 1)
})
}
})
test('afterReset and Rate Limit remain the same when enableDraftSpec is enabled', t => {
t.plan(16)
const fastify = Fastify()
fastify.register(rateLimit, {
max: 1,
timeWindow: '10s',
enableDraftSpec: true
})
fastify.get('/', (req, reply) => {
reply.send('hello!')
})
fastify.inject('/', (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
t.strictEqual(res.headers['ratelimit-limit'], 1)
t.strictEqual(res.headers['ratelimit-remaining'], 0)
setTimeout(retry.bind(null, 9), 500)
setTimeout(retry.bind(null, 8), 1500)
})
function retry (timeLeft) {
fastify.inject('/', (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 429)
t.strictEqual(res.headers['ratelimit-limit'], 1)
t.strictEqual(res.headers['ratelimit-remaining'], 0)
t.strictEqual(res.headers['ratelimit-reset'], timeLeft)
t.strictEqual(res.headers['ratelimit-reset'], res.headers['retry-after'])
})
}
})

@@ -979,1 +979,24 @@ 'use strict'

})
test('With enable IETF draft spec', t => {
t.plan(5)
const fastify = Fastify()
fastify.register(rateLimit, {
global: false,
enableDraftSpec: true
})
fastify.get('/', {
config: defaultRouteConfig
}, (req, reply) => {
reply.send('hello!')
})
fastify.inject('/', (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
t.strictEqual(res.headers['ratelimit-limit'], 2)
t.strictEqual(res.headers['ratelimit-remaining'], 1)
t.strictEqual(res.headers['ratelimit-reset'], 1)
})
})
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc