diff --git a/src/middleware/route-ratelimit.js b/src/middleware/route-ratelimit.js index 5bc6886..2bb2c2b 100644 --- a/src/middleware/route-ratelimit.js +++ b/src/middleware/route-ratelimit.js @@ -1,10 +1,13 @@ /* This file controls the request-per-minute (RPM) rate limits. - It is assumed that this middleware is run AFTER the auth.js middleware which - checks for Basic auth. If the user adds the correct Basic auth to the header - of their API request, they will get pro-tier rate limits. By default, the - freemium rate limits apply. + It is assumed that this middleware is run AFTER the jwt-auth.js and auth.js + middleware. + + Current rate limiting rules in requests-per-minute: + - anonymous access: 3 + - free access: 10 + - any paid tier: 100 */ "use strict" @@ -16,10 +19,10 @@ const axios = require("axios") // Set max requests per minute const maxRequests = process.env.RATE_LIMIT_MAX_REQUESTS ? parseInt(process.env.RATE_LIMIT_MAX_REQUESTS) - : 10 + : 3 // 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 const uniqueRateLimits = {} @@ -87,6 +90,9 @@ const routeRateLimit = async function(req, res, next) { // TODO: replace the console.logs with calls to our logging system. //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 if (!uniqueRateLimits[route]) { uniqueRateLimits[route] = new RateLimit({ diff --git a/test/v3/rate-limits.js b/test/v3/rate-limits.js index 4459e4a..3b2db1f 100644 --- a/test/v3/rate-limits.js +++ b/test/v3/rate-limits.js @@ -62,12 +62,67 @@ describe("#route-ratelimits", () => { 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.path = "/control/getNetworkInfo" 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. await routeRateLimit(req, res, next) @@ -95,11 +150,7 @@ describe("#route-ratelimits", () => { req.method = "GET" req.locals.proLimit = true - - //console.log(`req.locals before test: ${util.inspect(req.locals)}`) - - // Prepare the authorization header - //req.headers.authorization = generateAuthHeader("BITBOX") + req.locals.apiLevel = 10 for (let i = 0; i < 25; i++) { next.reset() // reset the stubbed next() function. @@ -131,13 +182,9 @@ describe("#route-ratelimits", () => { req.method = "GET" 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 < 400; i++) { + for (let i = 0; i < 150; i++) { next.reset() // reset the stubbed next() function. await routeRateLimit(req, res, next)