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. 6
      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

6
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 () {
if (!opts.skipHeaders) {
res.set('X-RateLimit-Limit', limit.total) res.set('X-RateLimit-Limit', limit.total)
res.set('X-RateLimit-Remaining', limit.remaining) 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