sparrow-controllers
Version:
Collection of platform-agnostic modules for creating secure data models for cryptocurrency wallets
110 lines • 4.84 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RateLimitController = void 0;
const eth_rpc_errors_1 = require("eth-rpc-errors");
const BaseControllerV2_1 = require("../BaseControllerV2");
const name = 'RateLimitController';
const metadata = {
requests: { persist: false, anonymous: false },
};
/**
* Controller with logic for rate-limiting API endpoints per requesting origin.
*/
class RateLimitController extends BaseControllerV2_1.BaseController {
/**
* Creates a RateLimitController instance.
*
* @param options - Constructor options.
* @param options.messenger - A reference to the messaging system.
* @param options.state - Initial state to set on this controller.
* @param options.implementations - Mapping from API type to API implementation.
* @param options.rateLimitTimeout - The time window in which the rate limit is applied (in ms).
* @param options.rateLimitCount - The amount of calls an origin can make in the rate limit time window.
*/
constructor({ rateLimitTimeout = 5000, rateLimitCount = 1, messenger, state, implementations, }) {
const defaultState = {
requests: Object.keys(implementations).reduce((acc, key) => (Object.assign(Object.assign({}, acc), { [key]: {} })), {}),
};
super({
name,
metadata,
messenger,
state: Object.assign(Object.assign({}, defaultState), state),
});
this.implementations = implementations;
this.rateLimitTimeout = rateLimitTimeout;
this.rateLimitCount = rateLimitCount;
this.messagingSystem.registerActionHandler(`${name}:call`, ((origin, type, ...args) => this.call(origin, type, ...args)));
}
/**
* Calls an API if the requesting origin is not rate-limited.
*
* @param origin - The requesting origin.
* @param type - The type of API call to make.
* @param args - Arguments for the API call.
* @returns `false` if rate-limited, and `true` otherwise.
*/
call(origin, type, ...args) {
return __awaiter(this, void 0, void 0, function* () {
if (this.isRateLimited(type, origin)) {
throw eth_rpc_errors_1.ethErrors.rpc.limitExceeded({
message: `"${type}" is currently rate-limited. Please try again later.`,
});
}
this.recordRequest(type, origin);
const implementation = this.implementations[type];
if (!implementation) {
throw new Error('Invalid api type');
}
return implementation(...args);
});
}
/**
* Checks whether an origin is rate limited for the a specific API.
*
* @param api - The API the origin is trying to access.
* @param origin - The origin trying to access the API.
* @returns `true` if rate-limited, and `false` otherwise.
*/
isRateLimited(api, origin) {
return this.state.requests[api][origin] >= this.rateLimitCount;
}
/**
* Records that an origin has made a request to call an API, for rate-limiting purposes.
*
* @param api - The API the origin is trying to access.
* @param origin - The origin trying to access the API.
*/
recordRequest(api, origin) {
this.update((state) => {
var _a;
const previous = (_a = state.requests[api][origin]) !== null && _a !== void 0 ? _a : 0;
state.requests[api][origin] = previous + 1;
if (previous === 0) {
setTimeout(() => this.resetRequestCount(api, origin), this.rateLimitTimeout);
}
});
}
/**
* Resets the request count for a given origin and API combination, for rate-limiting purposes.
*
* @param api - The API in question.
* @param origin - The origin in question.
*/
resetRequestCount(api, origin) {
this.update((state) => {
state.requests[api][origin] = 0;
});
}
}
exports.RateLimitController = RateLimitController;
//# sourceMappingURL=RateLimitController.js.map