Files
shields/lib/sys/rate-limit.js
Paul Melnikow 7a664ca3e8 Run prettier (#1866)
Merging this separately so the commit with the tooling change is readable. This is a follow-on to #1167 which turned prettier on.
2018-08-08 17:57:14 -04:00

56 lines
1.4 KiB
JavaScript

'use strict'
// A rate limit ensures that a request parameter gets flagged if it goes
// above a limit.
module.exports = class RateLimit {
constructor(options = {}) {
// this.hits: Map from request parameters to the number of hits.
this.hits = new Map()
this.period = options.period || 200 // 3 min ⅓, in seconds
this.maxHitsPerPeriod = options.maxHitsPerPeriod || 500
this.banned = new Set()
this.bannedUrls = new Set()
this.whitelist = options.whitelist || /(?!)/ // Matches nothing by default.
setInterval(this.resetHits.bind(this), this.period * 1000)
}
resetHits() {
this.hits.clear()
this.banned.clear()
this.bannedUrls.clear()
}
isBanned(reqParam, req, res) {
const hitsInCurrentPeriod = this.hits.get(reqParam) || 0
if (
reqParam != null &&
!this.whitelist.test(reqParam) &&
hitsInCurrentPeriod > this.maxHitsPerPeriod
) {
this.banned.add(reqParam)
}
if (this.banned.has(reqParam)) {
res.statusCode = 429
res.setHeader('Retry-After', String(this.period))
res.end(
`Exceeded limit ${this.maxHitsPerPeriod} requests ` +
`per ${this.period} seconds`
)
this.bannedUrls.add(req.url)
return true
}
this.hits.set(reqParam, hitsInCurrentPeriod + 1)
return false
}
toJSON() {
return {
banned: [...this.banned],
hits: [...this.hits],
urls: [...this.bannedUrls],
}
}
}