Skip to content

Instantly share code, notes, and snippets.

@danneu
Last active October 14, 2016 06:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danneu/0383504e01fb48d9fef40b450488d203 to your computer and use it in GitHub Desktop.
Save danneu/0383504e01fb48d9fef40b450488d203 to your computer and use it in GitHub Desktop.
// DEMO
const gate = new FloodGate()
socket.on('new_message', (text, cb) => {
// bail if user is spamming us
if (gate.bump(socket.ip).isFlooding(socket.ip)) {
return cb('RATE_LIMITED')
}
db.insertMessage(text).then(() => cb())
})
// FLOODGATE
function FloodGate () {
// map of ipAddress -> [Date]
this.store = {}
// rate.count = max amount of messages user can send since rate.seconds ago
this.rates = [
{count: 2, seconds: 1},
{count: 5, seconds: 5},
{count: 8, seconds: 10}
]
// the amount of timestamps we should track per key
this.bufferSize = this.rates.reduce((memo, {count}) => Math.max(memo, count), 0)
}
// =========================================================
FloodGate.prototype.isFlooding = function (key) {
if (!this.store[key]) {
return false
}
return this.rates.find((rate) => {
const after = new Date(Date.now() - rate.seconds * 1000)
const timestamps = this.store[key].filter((t) => t > after)
return timestamps.length >= rate.count
})
}
// =========================================================
// register a hit
FloodGate.prototype.bump = function(key) {
if (!this.store[key]) this.store[key] = []
this.store[key].push(new Date())
if (this.store[key].length > this.bufferSize) this.store[key].shift()
return this
}
// =========================================================
module.exports = FloodGate
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment