From 1d1c847b793620acf3a2b193ab45eabf53234cb2 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Tue, 8 Mar 2022 19:19:29 +0100 Subject: wallet: throttle all http requests even from browsers / service workers --- .../taler-wallet-core/src/headless/NodeHttpLib.ts | 2 +- .../taler-wallet-core/src/util/RequestThrottler.ts | 156 --------------------- packages/taler-wallet-core/tsconfig.json | 2 +- 3 files changed, 2 insertions(+), 158 deletions(-) delete mode 100644 packages/taler-wallet-core/src/util/RequestThrottler.ts (limited to 'packages/taler-wallet-core') diff --git a/packages/taler-wallet-core/src/headless/NodeHttpLib.ts b/packages/taler-wallet-core/src/headless/NodeHttpLib.ts index 5a90994b1..2a8c9e36c 100644 --- a/packages/taler-wallet-core/src/headless/NodeHttpLib.ts +++ b/packages/taler-wallet-core/src/headless/NodeHttpLib.ts @@ -25,7 +25,7 @@ import { HttpRequestOptions, HttpResponse, } from "../util/http.js"; -import { RequestThrottler } from "../util/RequestThrottler.js"; +import { RequestThrottler } from "@gnu-taler/taler-util"; import Axios, { AxiosResponse } from "axios"; import { OperationFailedError, makeErrorDetails } from "../errors.js"; import { Logger, bytesToString } from "@gnu-taler/taler-util"; diff --git a/packages/taler-wallet-core/src/util/RequestThrottler.ts b/packages/taler-wallet-core/src/util/RequestThrottler.ts deleted file mode 100644 index d79afe47a..000000000 --- a/packages/taler-wallet-core/src/util/RequestThrottler.ts +++ /dev/null @@ -1,156 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2019 GNUnet e.V. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING. If not, see - */ - -/** - * Implementation of token bucket throttling. - */ - -/** - * Imports. - */ -import { - getTimestampNow, - timestampDifference, - timestampCmp, - Logger, - URL, -} from "@gnu-taler/taler-util"; - -const logger = new Logger("RequestThrottler.ts"); - -/** - * Maximum request per second, per origin. - */ -const MAX_PER_SECOND = 100; - -/** - * Maximum request per minute, per origin. - */ -const MAX_PER_MINUTE = 500; - -/** - * Maximum request per hour, per origin. - */ -const MAX_PER_HOUR = 2000; - -/** - * Throttling state for one origin. - */ -class OriginState { - tokensSecond: number = MAX_PER_SECOND; - tokensMinute: number = MAX_PER_MINUTE; - tokensHour: number = MAX_PER_HOUR; - private lastUpdate = getTimestampNow(); - - private refill(): void { - const now = getTimestampNow(); - if (timestampCmp(now, this.lastUpdate) < 0) { - // Did the system time change? - this.lastUpdate = now; - return; - } - const d = timestampDifference(now, this.lastUpdate); - if (d.d_ms === "forever") { - throw Error("assertion failed"); - } - this.tokensSecond = Math.min( - MAX_PER_SECOND, - this.tokensSecond + d.d_ms / 1000, - ); - this.tokensMinute = Math.min( - MAX_PER_MINUTE, - this.tokensMinute + d.d_ms / 1000 / 60, - ); - this.tokensHour = Math.min( - MAX_PER_HOUR, - this.tokensHour + d.d_ms / 1000 / 60 / 60, - ); - this.lastUpdate = now; - } - - /** - * Return true if the request for this origin should be throttled. - * Otherwise, take a token out of the respective buckets. - */ - applyThrottle(): boolean { - this.refill(); - if (this.tokensSecond < 1) { - logger.warn("request throttled (per second limit exceeded)"); - return true; - } - if (this.tokensMinute < 1) { - logger.warn("request throttled (per minute limit exceeded)"); - return true; - } - if (this.tokensHour < 1) { - logger.warn("request throttled (per hour limit exceeded)"); - return true; - } - this.tokensSecond--; - this.tokensMinute--; - this.tokensHour--; - return false; - } -} - -/** - * Request throttler, used as a "last layer of defense" when some - * other part of the re-try logic is broken and we're sending too - * many requests to the same exchange/bank/merchant. - */ -export class RequestThrottler { - private perOriginInfo: { [origin: string]: OriginState } = {}; - - /** - * Get the throttling state for an origin, or - * initialize if no state is associated with the - * origin yet. - */ - private getState(origin: string): OriginState { - const s = this.perOriginInfo[origin]; - if (s) { - return s; - } - const ns = (this.perOriginInfo[origin] = new OriginState()); - return ns; - } - - /** - * Apply throttling to a request. - * - * @returns whether the request should be throttled. - */ - applyThrottle(requestUrl: string): boolean { - const origin = new URL(requestUrl).origin; - return this.getState(origin).applyThrottle(); - } - - /** - * Get the throttle statistics for a particular URL. - */ - getThrottleStats(requestUrl: string): Record { - const origin = new URL(requestUrl).origin; - const state = this.getState(origin); - return { - tokensHour: state.tokensHour, - tokensMinute: state.tokensMinute, - tokensSecond: state.tokensSecond, - maxTokensHour: MAX_PER_HOUR, - maxTokensMinute: MAX_PER_MINUTE, - maxTokensSecond: MAX_PER_SECOND, - }; - } -} diff --git a/packages/taler-wallet-core/tsconfig.json b/packages/taler-wallet-core/tsconfig.json index 3da332364..c3366373e 100644 --- a/packages/taler-wallet-core/tsconfig.json +++ b/packages/taler-wallet-core/tsconfig.json @@ -21,7 +21,7 @@ "esModuleInterop": true, "importHelpers": true, "rootDir": "./src", - "typeRoots": ["./node_modules/@types"], + "typeRoots": ["./node_modules/@types"] }, "references": [ { -- cgit v1.2.3