UNPKG

@balte/emberplus-connection

Version:
170 lines (169 loc) 7.43 kB
"use strict"; 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()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const net_1 = __importDefault(require("net")); const S101Socket_1 = __importDefault(require("./S101Socket")); const Client_1 = require("../Client"); const DEFAULT_PORT = 9000; const RECONNECT_ATTEMPTS = 60; const AUTO_RECONNECT_DELAY = 5000; class S101Client extends S101Socket_1.default { /** * * @param {string} address * @param {number} port=9000 */ constructor(address, port = DEFAULT_PORT, autoConnect) { super(); this.autoConnect = false; this._autoReconnect = true; this._autoReconnectDelay = AUTO_RECONNECT_DELAY; this._connectionAttemptTimer = undefined; this._reconnectAttempt = 0; this._reconnectAttempts = RECONNECT_ATTEMPTS; this._lastConnectionAttempt = 0; this.address = address; this.port = port; this.autoConnect = !!autoConnect; this._shouldBeConnected = this.autoConnect; if (this.autoConnect) this.connect(); } connect(timeout = 5) { return new Promise((resolve) => { if (this.status !== Client_1.ConnectionStatus.Disconnected) { // TODO - perhaps we should reconnect when addresses/ports have changed resolve(); return; } if (!this._lastConnectionAttempt || Date.now() - this._lastConnectionAttempt >= this._autoReconnectDelay) { // !_lastReconnectionAttempt means first attempt, OR > _reconnectionDelay since last attempt // recreates client if new attempt if (this.socket && this.socket.connecting) { this.socket.destroy(); this.socket.removeAllListeners(); delete this.socket; // @todo: fire event telling it gives up! } // (re)creates client, either on first run or new attempt if (!this.socket) { this.socket = new net_1.default.Socket(); this.socket.on('close', (hadError) => this._onClose(hadError)); this.socket.on('connect', () => this._onConnect()); this.socket.on('data', (data) => { try { this.codec.dataIn(data); } catch (e) { this.emit('error', e); } }); this.socket.on('error', (error) => this._onError(error)); } this.emit('connecting'); this.status = Client_1.ConnectionStatus.Disconnected; const connectTimeoutListener = () => { if (this.socket) { this.socket.destroy(); this.socket.removeAllListeners(); delete this.socket; } const reason = new Error(`Could not connect to ${this.address}:${this.port} after a timeout of ${timeout} seconds`); resolve(reason); if (!this._connectionAttemptTimer) this.connect(); }; const timer = setTimeout(() => connectTimeoutListener(), timeout * 1000); this.socket.connect(this.port, this.address); this.socket.once('connect', () => { clearInterval(timer); resolve(); }); this._shouldBeConnected = true; this._lastConnectionAttempt = Date.now(); } // sets timer to retry when needed if (!this._connectionAttemptTimer) { this._connectionAttemptTimer = setInterval(() => this._autoReconnectionAttempt(), this._autoReconnectDelay); } }); } disconnect(timeout) { const _super = Object.create(null, { disconnect: { get: () => super.disconnect } }); return __awaiter(this, void 0, void 0, function* () { this._shouldBeConnected = false; return _super.disconnect.call(this, timeout); }); } handleClose() { var _a; if (this.keepaliveIntervalTimer) clearInterval(this.keepaliveIntervalTimer); (_a = this.socket) === null || _a === void 0 ? void 0 : _a.destroy(); } _autoReconnectionAttempt() { if (this._autoReconnect) { if (this._reconnectAttempts > 0) { // no reconnection if no valid reconnectionAttemps is set if (this._reconnectAttempt >= this._reconnectAttempts) { // if current attempt is not less than max attempts // reset reconnection behaviour this._clearConnectionAttemptTimer(); this.status = Client_1.ConnectionStatus.Disconnected; return; } // new attempt if not already connected if (this.status !== Client_1.ConnectionStatus.Connected) { this._reconnectAttempt++; this.connect(); } } } } _clearConnectionAttemptTimer() { // @todo create event telling reconnection ended with result: true/false // only if reconnection interval is true this._reconnectAttempt = 0; if (this._connectionAttemptTimer) clearInterval(this._connectionAttemptTimer); delete this._connectionAttemptTimer; } _onConnect() { this._clearConnectionAttemptTimer(); this.startKeepAlive(); // this._sentKeepalive = Date.now() // this._receivedKeepalive = this._sentKeepalive + 1 // for some reason keepalive doesn't return directly after conn. this.status = Client_1.ConnectionStatus.Connected; this.emit('connected'); } _onError(error) { if (error.message.match(/ECONNREFUSED/)) { return; // we handle this internally through reconnections } this.emit('error', error); } _onClose(_hadError) { if (this.status !== Client_1.ConnectionStatus.Disconnected) this.emit('disconnected'); this.status = Client_1.ConnectionStatus.Disconnected; if (this._shouldBeConnected === true) { this.emit('connecting'); this.connect(); } } } exports.default = S101Client;