UNPKG

1.36 kBJavaScriptView Raw
1
2module.exports = createRatelimit
3
4function createRatelimit(opts) {
5 opts = opts || {}
6
7 var counters = {}
8 , limit = opts.limit || 1000
9 , steps = opts.steps || 60
10 , field = opts.field || "ip"
11 , penalty = opts.penalty || 5000
12 , tickTime = ((opts.time || 60*60*1000)/steps)|0
13 , leak = Math.ceil(limit/steps)
14 , penaltyLeak = Math.ceil(penalty/leak)
15 , nulled = 0
16 , warn = limit - leak
17
18 setInterval(tick, tickTime).unref()
19
20 return function ratelimit(req, res, next) {
21 var remaining
22 , key = req[field]
23
24 if (warn < (counters[key] > 0 ? ++counters[key] : (counters[key] = 1))) {
25 res.setHeader("Rate-Limit", limit)
26
27 remaining = limit - counters[key]
28 if (remaining < 0) {
29 res.setHeader("Retry-After", tickTime * Math.ceil(-remaining/leak))
30 setTimeout(block, penalty, res)
31 } else {
32 res.setHeader("Rate-Limit-Remaining", remaining)
33 setTimeout(next, penalty - penaltyLeak - remaining * penaltyLeak)
34 }
35 } else {
36 next()
37 }
38 }
39
40 function block(res) {
41 res.statusCode = 429
42 res.end("Too Many Requests")
43 }
44
45 function tick() {
46 var key, next
47 , curr = counters
48
49 if (nulled > 1000) {
50 nulled = 0
51 counters = next = {}
52 for (key in curr) if (curr[key] > leak) {
53 next[key] = curr[key] - leak
54 }
55 } else {
56 for (key in curr) if (curr[key] > 0) {
57 if ((curr[key] -= leak) <= 0) nulled++
58 }
59 }
60 }
61}
62
63
64