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 MessagesController = require('./messages');
|
||||||
var UtilsController = require('./utils');
|
var UtilsController = require('./utils');
|
||||||
var CurrencyController = require('./currency');
|
var CurrencyController = require('./currency');
|
||||||
|
var RateLimiter = require('./ratelimiter');
|
||||||
var morgan = require('morgan');
|
var morgan = require('morgan');
|
||||||
var bitcore = require('bitcore-lib');
|
var bitcore = require('bitcore-lib');
|
||||||
var _ = bitcore.deps._;
|
var _ = bitcore.deps._;
|
||||||
@ -107,6 +108,10 @@ InsightAPI.prototype.createLogInfoStream = function() {
|
|||||||
|
|
||||||
InsightAPI.prototype.setupRoutes = function(app) {
|
InsightAPI.prototype.setupRoutes = function(app) {
|
||||||
|
|
||||||
|
//Enable rate limiter
|
||||||
|
var limiter = new RateLimiter({node: this.node});
|
||||||
|
app.use(limiter.middleware());
|
||||||
|
|
||||||
//Setup logging
|
//Setup logging
|
||||||
var logFormat = ':remote-addr ":method :url" :status :res[content-length] :response-time ":user-agent" ';
|
var logFormat = ':remote-addr ":method :url" :status :res[content-length] :response-time ":user-agent" ';
|
||||||
var logStream = this.createLogInfoStream();
|
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