feat(rate-limit tiers): Created finer-resolution rate limits + unit tests

This commit is contained in:
Chris Troutner
2019-10-31 14:15:53 -07:00
parent 41d048f6a5
commit 17525a40d5
2 changed files with 72 additions and 19 deletions
+12 -6
View File
@@ -1,10 +1,13 @@
/* /*
This file controls the request-per-minute (RPM) rate limits. This file controls the request-per-minute (RPM) rate limits.
It is assumed that this middleware is run AFTER the auth.js middleware which It is assumed that this middleware is run AFTER the jwt-auth.js and auth.js
checks for Basic auth. If the user adds the correct Basic auth to the header middleware.
of their API request, they will get pro-tier rate limits. By default, the
freemium rate limits apply. Current rate limiting rules in requests-per-minute:
- anonymous access: 3
- free access: 10
- any paid tier: 100
*/ */
"use strict" "use strict"
@@ -16,10 +19,10 @@ const axios = require("axios")
// Set max requests per minute // Set max requests per minute
const maxRequests = process.env.RATE_LIMIT_MAX_REQUESTS const maxRequests = process.env.RATE_LIMIT_MAX_REQUESTS
? parseInt(process.env.RATE_LIMIT_MAX_REQUESTS) ? parseInt(process.env.RATE_LIMIT_MAX_REQUESTS)
: 10 : 3
// Pro-tier rate limits are 10x the freemium limits. // Pro-tier rate limits are 10x the freemium limits.
const PRO_RPM = 10 * maxRequests // const PRO_RPM = 10 * maxRequests
// Unique route mapped to its rate limit // Unique route mapped to its rate limit
const uniqueRateLimits = {} const uniqueRateLimits = {}
@@ -87,6 +90,9 @@ const routeRateLimit = async function(req, res, next) {
// TODO: replace the console.logs with calls to our logging system. // TODO: replace the console.logs with calls to our logging system.
//console.log(`applying pro-rate limits`) //console.log(`applying pro-rate limits`)
let PRO_RPM = 10 // Default value for free tier
if (req.locals.apiLevel > 0) PRO_RPM = 100 // RPM for paid tiers.
// Create new RateLimit if none exists for this route // Create new RateLimit if none exists for this route
if (!uniqueRateLimits[route]) { if (!uniqueRateLimits[route]) {
uniqueRateLimits[route] = new RateLimit({ uniqueRateLimits[route] = new RateLimit({
+60 -13
View File
@@ -62,12 +62,67 @@ describe("#route-ratelimits", () => {
assert.equal(next.called, true) assert.equal(next.called, true)
}) })
it("should trigger rate-limit handler if rate limits exceeds 25 request per minute", async () => { it("should trigger rate-limit handler if rate limits exceeds 5 request per minute", async () => {
req.baseUrl = "/v3" req.baseUrl = "/v3"
req.path = "/control/getNetworkInfo" req.path = "/control/getNetworkInfo"
req.method = "GET" req.method = "GET"
for (let i = 0; i < 35; i++) { for (let i = 0; i < 5; i++) {
next.reset() // reset the stubbed next() function.
await routeRateLimit(req, res, next)
//console.log(`next() called: ${next.called}`)
}
// Note: next() will be called unless the rate-limit kicks in.
assert.equal(
next.called,
false,
`next should not be called if rate limit was triggered.`
)
})
it("should NOT trigger rate-limit for free-tier at 5 RPM", async () => {
// Clear the require cache before running this test.
delete require.cache[
require.resolve("../../src/middleware/route-ratelimit")
]
rateLimitMiddleware = require("../../src/middleware/route-ratelimit")
routeRateLimit = rateLimitMiddleware.routeRateLimit
req.baseUrl = "/v3"
req.path = "/control/getNetworkInfo"
req.method = "GET"
req.locals.proLimit = true
req.locals.apiLevel = 0
for (let i = 0; i < 5; i++) {
next.reset() // reset the stubbed next() function.
await routeRateLimit(req, res, next)
//console.log(`next() called: ${next.called}`)
}
//console.log(`req.locals after test: ${util.inspect(req.locals)}`)
// Note: next() will be called unless the rate-limit kicks in.
assert.equal(
next.called,
true,
`next should be called if rate limit was not triggered.`
)
})
it("should trigger rate-limit for free tier 10 RPM", async () => {
req.baseUrl = "/v3"
req.path = "/control/getNetworkInfo"
req.method = "GET"
req.locals.proLimit = true
req.locals.apiLevel = 0
for (let i = 0; i < 12; i++) {
next.reset() // reset the stubbed next() function. next.reset() // reset the stubbed next() function.
await routeRateLimit(req, res, next) await routeRateLimit(req, res, next)
@@ -95,11 +150,7 @@ describe("#route-ratelimits", () => {
req.method = "GET" req.method = "GET"
req.locals.proLimit = true req.locals.proLimit = true
req.locals.apiLevel = 10
//console.log(`req.locals before test: ${util.inspect(req.locals)}`)
// Prepare the authorization header
//req.headers.authorization = generateAuthHeader("BITBOX")
for (let i = 0; i < 25; i++) { for (let i = 0; i < 25; i++) {
next.reset() // reset the stubbed next() function. next.reset() // reset the stubbed next() function.
@@ -131,13 +182,9 @@ describe("#route-ratelimits", () => {
req.method = "GET" req.method = "GET"
req.locals.proLimit = true req.locals.proLimit = true
req.locals.apiLevel = 10
//console.log(`req.locals before test: ${util.inspect(req.locals)}`) for (let i = 0; i < 150; i++) {
// Prepare the authorization header
//req.headers.authorization = generateAuthHeader("BITBOX")
for (let i = 0; i < 400; i++) {
next.reset() // reset the stubbed next() function. next.reset() // reset the stubbed next() function.
await routeRateLimit(req, res, next) await routeRateLimit(req, res, next)