UNPKG

hfs

Version:
108 lines (107 loc) 4.92 kB
"use strict"; // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.totalInSpeed = exports.totalOutSpeed = exports.totalGot = exports.totalSent = exports.throttler = void 0; exports.roundSpeed = roundSpeed; const stream_1 = require("stream"); const ThrottledStream_1 = require("./ThrottledStream"); const config_1 = require("./config"); const misc_1 = require("./misc"); const connections_1 = require("./connections"); const lodash_1 = __importDefault(require("lodash")); const events_1 = __importDefault(require("./events")); const persistence_1 = require("./persistence"); const mainThrottleGroup = new ThrottledStream_1.ThrottleGroup(Infinity); (0, config_1.defineConfig)('max_kbps', Infinity).sub(v => mainThrottleGroup.updateLimit(v)); const ip2group = {}; const SymThrStr = Symbol('stream'); const SymTimeout = Symbol('timeout'); const maxKbpsPerIp = (0, config_1.defineConfig)('max_kbps_per_ip', Infinity); maxKbpsPerIp.sub(v => { for (const [ip, { group }] of Object.entries(ip2group)) if (ip) // empty-string = unlimited group group.updateLimit(v); }); const throttler = async (ctx, next) => { var _a; var _b; await next(); let { body } = ctx; const downloadTotal = ctx.response.length; if (typeof body === 'string' || body && body instanceof Buffer) ctx.body = body = stream_1.Readable.from(body); if (!body || !(body instanceof stream_1.Readable)) return; // we wrap the stream also for unlimited connections to get speed and other features const noLimit = ((_a = ctx.state.account) === null || _a === void 0 ? void 0 : _a.ignore_limits) || (0, misc_1.isLocalHost)(ctx); const ipGroup = ip2group[_b = noLimit ? '' : ctx.ip] || (ip2group[_b] = { count: 0, group: new ThrottledStream_1.ThrottleGroup(noLimit ? Infinity : maxKbpsPerIp.get(), noLimit ? undefined : mainThrottleGroup), }); const conn = (0, connections_1.getConnection)(ctx); if (!conn) throw 'assert throttler connection'; const ts = conn[SymThrStr] = new ThrottledStream_1.ThrottledStream(ipGroup.group, conn[SymThrStr]); const offset = ts.getBytesSent(); let closed = false; const DELAY = 1000; const update = lodash_1.default.debounce(() => { const ts = conn[SymThrStr]; const outSpeed = roundSpeed(ts.getSpeed()); const { state } = ctx; (0, connections_1.updateConnection)(conn, { outSpeed, sent: conn.socket.bytesWritten }, { opProgress: state.opTotal && ((state.opOffset || 0) + (ts.getBytesSent() - offset) / state.opTotal) }); /* in case this stream stands still for a while (before the end), we'll have neither 'sent' or 'close' events, * so who will take care to updateConnection? This artificial next-call will ensure just that */ clearTimeout(conn[SymTimeout]); if (outSpeed || !closed) conn[SymTimeout] = setTimeout(update, DELAY); }, DELAY, { leading: true, maxWait: DELAY }); ts.on('sent', (n) => { exports.totalSent.set(x => x + n); update(); }); ++ipGroup.count; ts.on('close', () => { update.flush(); closed = true; if (--ipGroup.count) return; // any left? delete ip2group[ctx.ip]; }); ctx.state.originalStream = body; ctx.body = body.pipe(ts); if (downloadTotal !== undefined) // undefined will break SSE ctx.response.length = downloadTotal; // preserve this info ts.once('close', () => // in case of compressed response, we offer calculation of real size ctx.state.length = ts.getBytesSent() - offset); }; exports.throttler = throttler; function roundSpeed(n) { return lodash_1.default.round(n, 1) || lodash_1.default.round(n, 3); // further precision if necessary } exports.totalSent = persistence_1.storedMap.singleSync('totalSent', 0); exports.totalGot = persistence_1.storedMap.singleSync('totalGot', 0); exports.totalOutSpeed = 0; exports.totalInSpeed = 0; let lastSent; let lastGot; let last = Date.now(); setInterval(() => { const now = Date.now(); const past = now - last; last = now; { const v = exports.totalSent.get(); exports.totalOutSpeed = roundSpeed((v - (lastSent !== null && lastSent !== void 0 ? lastSent : v)) / past); lastSent = v; } { const v = exports.totalGot.get(); exports.totalInSpeed = roundSpeed((v - (lastGot !== null && lastGot !== void 0 ? lastGot : v)) / past); lastGot = v; } }, 1000); events_1.default.on('connection', (c) => c.socket.on('data', data => exports.totalGot.set(x => x + data.length)));