UNPKG

@ntrip/caster

Version:
167 lines (166 loc) 6.91 kB
"use strict"; /* * This file is part of the @ntrip/caster distribution (https://github.com/node-ntrip/caster). * Copyright (c) 2020 Nebojsa Cvetkovic. * * This program 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 of the License, or * (at your option) any later version. * * This program 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 this program. If not, see <https://www.gnu.org/licenses/>. */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Mountpoint = void 0; const stream = require("stream"); const sourcetable_1 = require("./sourcetable"); const events_1 = __importDefault(require("events")); const auto_source_entry_1 = require("./util/auto-source-entry"); const MOUNTPOINT_NAME_REGEX = /^[A-Za-z0-9-_.]{1,100}$/; const STATS_RATE_RUNNING_AVERAGE_ALPHA = 0.90; const CONNECTION_TIMEOUT_DEFAULT = 10000; const SILENCE_WARNING_TIMEOUT_DEFAULT = 10000; const SILENCE_TIMEOUT_DEFAULT = 30000; const DEFAULT_OPTIONS = { connectionTimeout: CONNECTION_TIMEOUT_DEFAULT, silenceTimeout: SILENCE_TIMEOUT_DEFAULT, silenceWarningTimeout: SILENCE_WARNING_TIMEOUT_DEFAULT, autoSourceEntry: true, serverSourceEntry: false }; class Mountpoint extends events_1.default.EventEmitter { constructor(name, options) { var _a; super(); this.hidden = false; this._server = null; this._clients = new Set(); this.connectionTimeout = null; this.silenceTimeout = null; this.silenceWarningTimeout = null; this.stats = { in: 0, out: 0, rate: 0 }; this.statsStream = new stream.Writable({ write: (chunk, encoding, callback) => { this.stats.in += chunk.length; this.stats.out += chunk.length * this._clients.size; callback(); this.resetTimeouts(); } }); this.statsRateCalculator = (old) => { const current = this.stats.in; const rate = current - old; this.stats.rate = Math.round(this.stats.rate * STATS_RATE_RUNNING_AVERAGE_ALPHA + rate * (1.0 - STATS_RATE_RUNNING_AVERAGE_ALPHA)); setTimeout(() => this.statsRateCalculator(current), 1000); }; this.options = options = { ...DEFAULT_OPTIONS, ...options }; if (!MOUNTPOINT_NAME_REGEX.test(name)) throw new Error(`Mountpoint name (${name}) contains invalid characters: must be 1..100 characters, A-Za-z0-9.-_`); this.name = name; this.sourceEntry = new sourcetable_1.Sourcetable.SourceEntry(name); if (options === null || options === void 0 ? void 0 : options.autoSourceEntry) this.autoSourceEntry = new auto_source_entry_1.AutoSourceEntry(this, (_a = this.options) === null || _a === void 0 ? void 0 : _a.autoSourceEntryOptions); this.statsRateCalculator(0); this.resetTimeouts(); } get server() { return this._server; } get clients() { return this._clients; } on(event, listener) { return super.on(event, listener); } resetTimeouts() { var _a, _b, _c; if (this.connectionTimeout !== null) clearTimeout(this.connectionTimeout); if (this.silenceTimeout !== null) clearTimeout(this.silenceTimeout); if (this.silenceWarningTimeout !== null) clearTimeout(this.silenceWarningTimeout); this.connectionTimeout = this.silenceTimeout = this.silenceWarningTimeout = null; if (this.active) { this.silenceTimeout = setTimeout(() => { this.emit('inactivity'); this.clearServer(); }, (_a = this.options) === null || _a === void 0 ? void 0 : _a.silenceTimeout); this.silenceWarningTimeout = setTimeout(() => { this.emit('silence'); }, (_b = this.options) === null || _b === void 0 ? void 0 : _b.silenceWarningTimeout); } else { this.connectionTimeout = setTimeout(() => { this.emit('timeout'); this.close(); }, (_c = this.options) === null || _c === void 0 ? void 0 : _c.connectionTimeout); } } get active() { return this._server != null; } setServer(server) { var _a, _b; if (this._server === server) return; if (this._server !== null) throw new Error(`Another server is already connected to mountpoint ${this.name}`); this._server = server; this.emit('server', server); server.once('close', () => this.clearServer(server)); this.resetTimeouts(); server.input.pipe(this.statsStream, { end: false }); this._clients.forEach((client) => server.pipe(client)); if ((_a = this.options) === null || _a === void 0 ? void 0 : _a.serverSourceEntry) server.on('str', (str) => this.sourceEntry.fromSourcetableLine('STR;;' + str)); if ((_b = this.options) === null || _b === void 0 ? void 0 : _b.autoSourceEntry) server.rtcm.pipe(this.autoSourceEntry, { end: false }); } clearServer(server = this._server) { if (this._server === null) return; if (this._server !== server) return; this._server = null; this.resetTimeouts(); server.input.unpipe(this.statsStream); this._clients.forEach((client) => server.unpipe(client)); if (this.autoSourceEntry !== undefined) server.rtcm.unpipe(this.autoSourceEntry); server.close(); } addClient(client) { var _a; if (this._clients.has(client)) return; this._clients.add(client); this.emit('client', client); client.on('close', () => this.removeClient(client)); (_a = this._server) === null || _a === void 0 ? void 0 : _a.pipe(client); } removeClient(client) { var _a; if (!this._clients.has(client)) return; this._clients.delete(client); (_a = this._server) === null || _a === void 0 ? void 0 : _a.unpipe(client); client.close(); } close() { this.clearServer(); this._clients.forEach((client) => this.removeClient(client)); this.emit('close'); } } exports.Mountpoint = Mountpoint;