This repository has been archived on 2024-03-03. You can view files and clone it, but cannot push or open issues or pull requests.
cors-anywhere-docker/lib/rate-limit.js

75 lines
3.1 KiB
JavaScript

'use strict';
module.exports = function createRateLimitChecker(CORSANYWHERE_RATELIMIT) {
// Configure rate limit. The following format is accepted for CORSANYWHERE_RATELIMIT:
// <max requests per period> <period in minutes> <non-ratelimited hosts>
// where <non-ratelimited hosts> is a space-separated list of strings or regexes (/.../) that
// matches the whole host (ports have to be listed explicitly if applicable).
// <period in minutes> cannot be zero.
//
// Examples:
// - Allow any origin to make one request per 5 minutes:
// 1 5
//
// - Allow example.com to make an unlimited number of requests, and the others 1 per 5 minutes.
// 1 5 example.com
//
// - Allow example.com, or any subdomain to make any number of requests and block the rest:
// 0 1 /(.*\.)?example\.com/
//
// - Allow example.com and www.example.com, and block the rest:
// 0 1 example.com www.example.com
var rateLimitConfig = /^(\d+) (\d+)(?:\s*$|\s+(.+)$)/.exec(CORSANYWHERE_RATELIMIT);
if (!rateLimitConfig) {
// No rate limit by default.
return function checkRateLimit() {};
}
var maxRequestsPerPeriod = parseInt(rateLimitConfig[1]);
var periodInMinutes = parseInt(rateLimitConfig[2]);
var unlimitedPattern = rateLimitConfig[3]; // Will become a RegExp or void.
if (unlimitedPattern) {
var unlimitedPatternParts = [];
unlimitedPattern.trim().split(/\s+/).forEach(function(unlimitedHost, i) {
var startsWithSlash = unlimitedHost.charAt(0) === '/';
var endsWithSlash = unlimitedHost.slice(-1) === '/';
if (startsWithSlash || endsWithSlash) {
if (unlimitedHost.length === 1 || !startsWithSlash || !endsWithSlash) {
throw new Error('Invalid CORSANYWHERE_RATELIMIT. Regex at index ' + i +
' must start and end with a slash ("/").');
}
unlimitedHost = unlimitedHost.slice(1, -1);
// Throws if the pattern is invalid.
new RegExp(unlimitedHost);
} else {
// Just escape RegExp characters even though they cannot appear in a host name.
// The only actual important escape is the dot.
unlimitedHost = unlimitedHost.replace(/[$()*+.?[\\\]^{|}]/g, '\\$&');
}
unlimitedPatternParts.push(unlimitedHost);
});
unlimitedPattern = new RegExp('^(?:' + unlimitedPatternParts.join('|') + ')$', 'i');
}
var accessedHosts = Object.create(null);
setInterval(function() {
accessedHosts = Object.create(null);
}, periodInMinutes * 60000);
var rateLimitMessage = 'The number of requests is limited to ' + maxRequestsPerPeriod +
(periodInMinutes === 1 ? ' per minute' : ' per ' + periodInMinutes + ' minutes') + '. ' +
'Please self-host CORS Anywhere if you need more quota. ' +
'See https://github.com/Rob--W/cors-anywhere#demo-server';
return function checkRateLimit(origin) {
var host = origin.replace(/^[\w\-]+:\/\//i, '');
if (unlimitedPattern && unlimitedPattern.test(host)) {
return;
}
var count = accessedHosts[host] || 0;
++count;
if (count > maxRequestsPerPeriod) {
return rateLimitMessage;
}
accessedHosts[host] = count;
};
};