Browse Source

now with tests :)

develop
Dustin Diaz 10 years ago
parent
commit
c63b459cff
  1. 34
      index.js
  2. 13
      package.json
  3. 72
      tests/index.js

34
index.js

@ -2,38 +2,44 @@ module.exports = function (app, db) {
return function (opts) {
app[opts.method](opts.path, function (req, res, next) {
opts.lookup = Array.isArray(opts.lookup) ? opts.lookup : [opts.lookup]
var lookups = opts.lookup.map(function (item) {
return item + item.split('.').reduce(function (prev, cur) {
return item + ':' + item.split('.').reduce(function (prev, cur) {
return prev[cur]
}, req)
}).join(':')
var key = 'ratelimit:' + path + ':' + method + ':' + lookups
var key = 'ratelimit:' + opts.path + ':' + opts.method + ':' + lookups
db.get(key, function (err, limit) {
var now = Date.now()
limit = limit ? JSON.parse(limit) : {
total: total,
remaining: total,
total: opts.total,
remaining: opts.total,
reset: now + opts.expire
}
if (now > limit.reset) limit.reset = now + opts.expire
if (now > limit.reset) {
limit.reset = now + opts.expire
limit.remaining = opts.total
}
limit.remaining = Number(limit.remaining) - 1
// do not allow negative remaining
limit.remaining = Math.max(Number(limit.remaining) - 1, 0)
db.set(key, JSON.stringify(limit)
db.set(key, JSON.stringify(limit), function () {
res.set('X-RateLimit-Limit', limit.total)
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)
res.send(429, 'Rate limit exceeded')
})
res.set('Retry-After', after)
res.send(429, 'Rate limit exceeded')
})
}
})
}
}

13
package.json

@ -17,9 +17,16 @@
},
"homepage": "https://github.com/ded/rate-limitter",
"devDependencies": {
"mocha": "~1.18.2",
"express": "~4.0.0-rc3",
"redis": "~0.10.1",
"express": "~3.5.0",
"chai": "~1.9.1"
"mocha": "~1.18.2",
"chai": "~1.9.1",
"sinon": "~1.9.0",
"sinon-chai": "~2.5.0",
"supertest": "~0.10.0",
"valentine": "~2.0.2"
},
"dependencies": {
"valentine": "~2.0.2"
}
}

72
tests/index.js

@ -1,6 +1,72 @@
var expect = require('chai').expect
var chai = require('chai')
, expect = chai.expect
, request = require('supertest')
, sinon = require('sinon')
, redis = require('redis').createClient()
, v = require('valentine')
, subject = require('../')
chai.use(require('sinon-chai'))
describe('rate-limitter', function () {
it('should work', function () {
expect(true).to.be.true
var express, app
beforeEach(function () {
express = require('express')
app = express()
limitter = subject(app, redis)
})
afterEach(function (done) {
redis.flushdb(done)
})
it('should work', function (done) {
var map = [10, 9, 8, 7, 6, 5, 4, 3, 2]
var clock = sinon.useFakeTimers()
limitter({
path: '/route',
method: 'get',
lookup: ['connection.remoteAddress'],
total: 10,
expire: 1000 * 60 * 60
})
app.get('/route', function (req, res) {
res.send(200, 'hello')
})
var out = (map).map(function (item) {
return function (f) {
process.nextTick(function () {
request(app)
.get('/route')
.expect('X-RateLimit-Limit', 10)
.expect('X-RateLimit-Remaining', item - 1)
.expect(200, function (e) {f(e)})
})
}
})
out.push(function (f) {
request(app)
.get('/route')
.expect('X-RateLimit-Limit', 10)
.expect('X-RateLimit-Remaining', 0)
.expect('Retry-After', /\d+/)
.expect(429, function (e) {f(e)})
})
out.push(function (f) {
// expire the time
clock.tick(1000 * 60 * 60 + 1)
request(app)
.get('/route')
.expect('X-RateLimit-Limit', 10)
.expect('X-RateLimit-Remaining', 9)
.expect(200, function (e) {
clock.restore()
f(e)
})
})
v.waterfall(out, done)
})
})

Loading…
Cancel
Save