Browse Source

Merge pull request #2 from roark31337/feature/skipHeaders

Add ability to set skipHeaders into the options to prevent the setting o...
develop
Dustin Diaz 11 years ago
parent
commit
2bb0855a74
  1. 14
      README.md
  2. 10
      index.js
  3. 48
      tests/index.js

14
README.md

@ -45,6 +45,8 @@ limiter(options)
- `total`: `Number` allowed number of requests before getting rate limited - `total`: `Number` allowed number of requests before getting rate limited
- `expire`: `Number` amount of time in `ms` before the rate-limited is reset - `expire`: `Number` amount of time in `ms` before the rate-limited is reset
- `whitelist`: `function(req)` optional param allowing the ability to whitelist. return `boolean`, `true` to whitelist, `false` to passthru to limiter. - `whitelist`: `function(req)` optional param allowing the ability to whitelist. return `boolean`, `true` to whitelist, `false` to passthru to limiter.
- `skipHeaders`: `Boolean` whether to skip sending HTTP headers for rate limits ()
- `ignoreErrors`: `Boolean` whether errors generated from redis should allow the middleware to call next(). Defaults to false.
### Examples ### Examples
@ -89,6 +91,18 @@ limiter({
return !!req.user.is_admin return !!req.user.is_admin
} }
}) })
// skip sending HTTP limit headers
limiter({
path: '/delete/thing',
method: 'post',
lookup: 'user.id',
whitelist: function (req) {
return !!req.user.is_admin
},
skipHeaders: true
})
``` ```
### as direct middleware ### as direct middleware

10
index.js

@ -12,6 +12,7 @@ module.exports = function (app, db) {
var key = 'ratelimit:' + opts.path + ':' + opts.method + ':' + lookups var key = 'ratelimit:' + opts.path + ':' + opts.method + ':' + lookups
db.get(key, function (err, limit) { db.get(key, function (err, limit) {
if (err && opts.ignoreErrors) return next()
var now = Date.now() var now = Date.now()
limit = limit ? JSON.parse(limit) : { limit = limit ? JSON.parse(limit) : {
total: opts.total, total: opts.total,
@ -28,14 +29,17 @@ module.exports = function (app, db) {
limit.remaining = Math.max(Number(limit.remaining) - 1, 0) limit.remaining = Math.max(Number(limit.remaining) - 1, 0)
db.set(key, JSON.stringify(limit), function () { db.set(key, JSON.stringify(limit), function () {
res.set('X-RateLimit-Limit', limit.total) if (!opts.skipHeaders) {
res.set('X-RateLimit-Remaining', limit.remaining) res.set('X-RateLimit-Limit', limit.total)
res.set('X-RateLimit-Remaining', limit.remaining)
}
if (limit.remaining) return next() if (limit.remaining) return next()
var after = (limit.reset - Date.now()) / 1000 var after = (limit.reset - Date.now()) / 1000
res.set('Retry-After', after) if (!opts.skipHeaders) res.set('Retry-After', after)
res.send(429, 'Rate limit exceeded') res.send(429, 'Rate limit exceeded')
}) })

48
tests/index.js

@ -24,6 +24,7 @@ describe('rate-limiter', function () {
it('should work', function (done) { it('should work', function (done) {
var map = [10, 9, 8, 7, 6, 5, 4, 3, 2] var map = [10, 9, 8, 7, 6, 5, 4, 3, 2]
var clock = sinon.useFakeTimers() var clock = sinon.useFakeTimers()
limiter({ limiter({
path: '/route', path: '/route',
method: 'get', method: 'get',
@ -69,4 +70,51 @@ describe('rate-limiter', function () {
}) })
v.waterfall(out, done) v.waterfall(out, done)
}) })
describe('options', function() {
it('should process skipHeaders', function (done) {
limiter({
path: '/route',
method: 'get',
lookup: ['connection.remoteAddress'],
total: 0,
expire: 1000 * 60 * 60,
skipHeaders: true
})
app.get('/route', function (req, res) {
res.send(200, 'hello')
})
request(app)
.get('/route')
.expect(function(res) { if ('X-RateLimit-Limit' in res.headers) return 'X-RateLimit-Limit Header not to be set' })
.expect(function(res) { if ('X-RateLimit-Remaining' in res.headers) return 'X-RateLimit-Remaining Header not to be set' })
.expect(function(res) { if ('Retry-After' in res.headers) return 'Retry-After not to be set' })
.expect(429, done)
})
it('should process ignoreErrors', function (done) {
limiter({
path: '/route',
method: 'get',
lookup: ['connection.remoteAddress'],
total: 10,
expire: 1000 * 60 * 60,
ignoreErrors: true
})
app.get('/route', function (req, res) {
res.send(200, 'hello')
})
sinon.stub(redis, 'get', function(key, callback) {
callback({err: true})
})
request(app)
.get('/route')
.expect(200, done)
})
})
}) })

Loading…
Cancel
Save