diff --git a/ratelimit.js b/ratelimit.js index b860a64..40489ee 100644 --- a/ratelimit.js +++ b/ratelimit.js @@ -1,12 +1,21 @@ var requests = {}; + function checkLimit(userId, res) { - if (requests[userId] == undefined) requests[userId] = 0; - requests[userId]++; - if (requests[userId] > 100) { - res.send("Rate limited: " + userId); + var now = Date.now(); + var windowMs = 60000; + if (!requests[userId]) requests[userId] = []; + var timestamps = requests[userId]; + timestamps.push(now); + while (timestamps.length > 0 && timestamps[0] < now - windowMs) { + timestamps.shift(); + } + if (timestamps.length > 100) { + res.status(429).send("Rate limited"); return false; } - try { logRequest(userId); } catch (e) {} return true; } -module.exports = { checkLimit }; + +function reset() { requests = {}; } + +module.exports = { checkLimit, reset }; diff --git a/smoke.test.js b/smoke.test.js index bde2569..c72ac40 100644 --- a/smoke.test.js +++ b/smoke.test.js @@ -1,3 +1,20 @@ const { validateEmail } = require('./validator.js'); +const { checkLimit, reset } = require('./ratelimit.js'); + if (validateEmail('a@b.co') !== true) { console.error('FAIL: valid email rejected'); process.exit(1); } -console.log('smoke ok'); process.exit(0); + +reset(); +var res = { statusCode: 0, status: function(c) { this.statusCode = c; return this; }, send: function(s) {} }; + +for (var i = 0; i < 100; i++) { + if (checkLimit('u1', res) !== true) { console.error('FAIL: request ' + (i+1) + ' blocked prematurely'); process.exit(1); } +} +if (checkLimit('u1', res) !== false) { console.error('FAIL: request 101 not blocked'); process.exit(1); } +if (res.statusCode !== 429) { console.error('FAIL: status not 429'); process.exit(1); } + +reset(); +for (var j = 0; j < 101; j++) checkLimit('uA', res); +if (checkLimit('uB', res) !== true) { console.error('FAIL: user isolation broken'); process.exit(1); } + +console.log('smoke ok'); +process.exit(0);