index: start of rate limiter
This commit is contained in:
parent
0957f2301e
commit
491a5cb846
@ -12,6 +12,7 @@ var StatusController = require('./status');
|
||||
var MessagesController = require('./messages');
|
||||
var UtilsController = require('./utils');
|
||||
var CurrencyController = require('./currency');
|
||||
var RateLimiter = require('./ratelimiter');
|
||||
var morgan = require('morgan');
|
||||
var bitcore = require('bitcore-lib');
|
||||
var _ = bitcore.deps._;
|
||||
@ -107,6 +108,10 @@ InsightAPI.prototype.createLogInfoStream = function() {
|
||||
|
||||
InsightAPI.prototype.setupRoutes = function(app) {
|
||||
|
||||
//Enable rate limiter
|
||||
var limiter = new RateLimiter({node: this.node});
|
||||
app.use(limiter.middleware());
|
||||
|
||||
//Setup logging
|
||||
var logFormat = ':remote-addr ":method :url" :status :res[content-length] :response-time ":user-agent" ';
|
||||
var logStream = this.createLogInfoStream();
|
||||
|
||||
132
lib/ratelimiter.js
Normal file
132
lib/ratelimiter.js
Normal file
@ -0,0 +1,132 @@
|
||||
'use strict';
|
||||
|
||||
var THREE_HOURS = 3* 60 * 60 * 1000;
|
||||
|
||||
/**
|
||||
* A rate limiter to be used as an express middleware.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {Number} options.limit - Number of requests for normal rate limiter
|
||||
* @param {Number} options.every - Interval of the normal rate limiter
|
||||
* @param {Array} options.whitelist - IP addresses that should have whitelist rate limiting
|
||||
* @param {Array} options.blacklist - IP addresses that should be blacklist rate limiting
|
||||
* @param {Number} options.whitelistLimit - Number of requests for whitelisted clients
|
||||
* @param {Number} options.whitelistEvery - Interval for whitelisted clients
|
||||
* @param {Number} options.blacklistLimit - Number of requests for blacklisted clients
|
||||
* @param {Number} options.blacklistEvery - Interval for blacklisted clients
|
||||
*/
|
||||
function RateLimiter(options) {
|
||||
if (!(this instanceof RateLimiter)) {
|
||||
return new RateLimiter(options);
|
||||
}
|
||||
|
||||
if (!options){
|
||||
options = {};
|
||||
}
|
||||
|
||||
this.node = options.node;
|
||||
this.clients = {};
|
||||
this.whitelist = options.whitelist || [];
|
||||
this.blacklist = options.blacklist || [];
|
||||
|
||||
this.config = {
|
||||
whitelist: {
|
||||
totalRequests: options.whitelistLimit || 3 * 60 * 60 * 10, // 108,000
|
||||
every: options.whitelistEvery || THREE_HOURS
|
||||
},
|
||||
blacklist: {
|
||||
totalRequests: options.blacklistLimit || 0,
|
||||
every: options.blacklistEvery || THREE_HOURS
|
||||
},
|
||||
normal: {
|
||||
totalRequests: options.limit || 3 * 60 * 60, // 10,800
|
||||
every: options.every || THREE_HOURS
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
RateLimiter.prototype.middleware = function() {
|
||||
var self = this;
|
||||
return function(req, res, next) {
|
||||
self._middleware(req, res, next);
|
||||
};
|
||||
};
|
||||
|
||||
RateLimiter.prototype._middleware = function(req, res, next) {
|
||||
|
||||
var name = this.getClientName(req);
|
||||
var client = this.clients[name];
|
||||
|
||||
res.ratelimit = {
|
||||
clients: this.clients,
|
||||
exceeded: false
|
||||
};
|
||||
|
||||
if (!client) {
|
||||
client = this.addClient(name);
|
||||
}
|
||||
|
||||
res.setHeader('X-RateLimit-Limit', this.config[client.type].totalRequests);
|
||||
res.setHeader('X-RateLimit-Remaining', this.config[client.type].totalRequests - client.visits);
|
||||
|
||||
res.ratelimit.exceeded = this.exceeded(client);
|
||||
res.ratelimit.client = client;
|
||||
|
||||
if (!this.exceeded(client)) {
|
||||
client.visits++;
|
||||
next();
|
||||
} else {
|
||||
this.node.log.warn('Rate limited:', client);
|
||||
res.status(429).jsonp({
|
||||
status: 429,
|
||||
error: 'Rate limit exceeded'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
RateLimiter.prototype.exceeded = function(client) {
|
||||
if (this.config[client.type].totalRequests === -1) {
|
||||
return false;
|
||||
} else {
|
||||
return client.visits > this.config[client.type].totalRequests;
|
||||
}
|
||||
};
|
||||
|
||||
RateLimiter.prototype.getClientType = function(name) {
|
||||
if (this.whitelist.indexOf(name) > -1) {
|
||||
return 'whitelist';
|
||||
}
|
||||
if (this.blacklist.indexOf(name) > -1) {
|
||||
return 'blacklist';
|
||||
}
|
||||
return 'normal';
|
||||
};
|
||||
|
||||
RateLimiter.prototype.getClientName = function(req) {
|
||||
var name = req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for'] || req.connection.remoteAddress;
|
||||
return name;
|
||||
};
|
||||
|
||||
RateLimiter.prototype.addClient = function(name) {
|
||||
var self = this;
|
||||
|
||||
var client = {
|
||||
name: name,
|
||||
type: this.getClientType(name),
|
||||
visits: 1
|
||||
};
|
||||
|
||||
var resetTime = this.config[client.type].every;
|
||||
|
||||
setTimeout(function() {
|
||||
delete self.clients[name];
|
||||
}, resetTime).unref();
|
||||
|
||||
this.clients[name] = client;
|
||||
|
||||
return client;
|
||||
|
||||
};
|
||||
|
||||
module.exports = RateLimiter;
|
||||
Loading…
Reference in New Issue
Block a user