forked from Rob--W/cors-anywhere
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrate-limit.js
74 lines (71 loc) · 3.1 KB
/
rate-limit.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
'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;
};
};