reconnecting-websocket-iife.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. var ReconnectingWebSocket = (function () {
  2. 'use strict';
  3. /*! *****************************************************************************
  4. Copyright (c) Microsoft Corporation. All rights reserved.
  5. Licensed under the Apache License, Version 2.0 (the "License"); you may not use
  6. this file except in compliance with the License. You may obtain a copy of the
  7. License at http://www.apache.org/licenses/LICENSE-2.0
  8. THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  9. KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
  10. WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
  11. MERCHANTABLITY OR NON-INFRINGEMENT.
  12. See the Apache Version 2.0 License for specific language governing permissions
  13. and limitations under the License.
  14. ***************************************************************************** */
  15. /* global Reflect, Promise */
  16. var extendStatics = function(d, b) {
  17. extendStatics = Object.setPrototypeOf ||
  18. ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
  19. function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  20. return extendStatics(d, b);
  21. };
  22. function __extends(d, b) {
  23. extendStatics(d, b);
  24. function __() { this.constructor = d; }
  25. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  26. }
  27. function __values(o) {
  28. var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0;
  29. if (m) return m.call(o);
  30. return {
  31. next: function () {
  32. if (o && i >= o.length) o = void 0;
  33. return { value: o && o[i++], done: !o };
  34. }
  35. };
  36. }
  37. function __read(o, n) {
  38. var m = typeof Symbol === "function" && o[Symbol.iterator];
  39. if (!m) return o;
  40. var i = m.call(o), r, ar = [], e;
  41. try {
  42. while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
  43. }
  44. catch (error) { e = { error: error }; }
  45. finally {
  46. try {
  47. if (r && !r.done && (m = i["return"])) m.call(i);
  48. }
  49. finally { if (e) throw e.error; }
  50. }
  51. return ar;
  52. }
  53. function __spread() {
  54. for (var ar = [], i = 0; i < arguments.length; i++)
  55. ar = ar.concat(__read(arguments[i]));
  56. return ar;
  57. }
  58. var Event = /** @class */ (function () {
  59. function Event(type, target) {
  60. this.target = target;
  61. this.type = type;
  62. }
  63. return Event;
  64. }());
  65. var ErrorEvent = /** @class */ (function (_super) {
  66. __extends(ErrorEvent, _super);
  67. function ErrorEvent(error, target) {
  68. var _this = _super.call(this, 'error', target) || this;
  69. _this.message = error.message;
  70. _this.error = error;
  71. return _this;
  72. }
  73. return ErrorEvent;
  74. }(Event));
  75. var CloseEvent = /** @class */ (function (_super) {
  76. __extends(CloseEvent, _super);
  77. function CloseEvent(code, reason, target) {
  78. if (code === void 0) { code = 1000; }
  79. if (reason === void 0) { reason = ''; }
  80. var _this = _super.call(this, 'close', target) || this;
  81. _this.wasClean = true;
  82. _this.code = code;
  83. _this.reason = reason;
  84. return _this;
  85. }
  86. return CloseEvent;
  87. }(Event));
  88. /*!
  89. * Reconnecting WebSocket
  90. * by Pedro Ladaria <pedro.ladaria@gmail.com>
  91. * https://github.com/pladaria/reconnecting-websocket
  92. * License MIT
  93. */
  94. var getGlobalWebSocket = function () {
  95. if (typeof WebSocket !== 'undefined') {
  96. // @ts-ignore
  97. return WebSocket;
  98. }
  99. };
  100. /**
  101. * Returns true if given argument looks like a WebSocket class
  102. */
  103. var isWebSocket = function (w) { return typeof w !== 'undefined' && !!w && w.CLOSING === 2; };
  104. var DEFAULT = {
  105. maxReconnectionDelay: 10000,
  106. minReconnectionDelay: 1000 + Math.random() * 4000,
  107. minUptime: 5000,
  108. reconnectionDelayGrowFactor: 1.3,
  109. connectionTimeout: 4000,
  110. maxRetries: Infinity,
  111. maxEnqueuedMessages: Infinity,
  112. startClosed: false,
  113. debug: false,
  114. };
  115. var ReconnectingWebSocket = /** @class */ (function () {
  116. function ReconnectingWebSocket(url, protocols, options) {
  117. var _this = this;
  118. if (options === void 0) { options = {}; }
  119. this._listeners = {
  120. error: [],
  121. message: [],
  122. open: [],
  123. close: [],
  124. };
  125. this._retryCount = -1;
  126. this._shouldReconnect = true;
  127. this._connectLock = false;
  128. this._binaryType = 'blob';
  129. this._closeCalled = false;
  130. this._messageQueue = [];
  131. /**
  132. * An event listener to be called when the WebSocket connection's readyState changes to CLOSED
  133. */
  134. this.onclose = null;
  135. /**
  136. * An event listener to be called when an error occurs
  137. */
  138. this.onerror = null;
  139. /**
  140. * An event listener to be called when a message is received from the server
  141. */
  142. this.onmessage = null;
  143. /**
  144. * An event listener to be called when the WebSocket connection's readyState changes to OPEN;
  145. * this indicates that the connection is ready to send and receive data
  146. */
  147. this.onopen = null;
  148. this._handleOpen = function (event) {
  149. _this._debug('open event');
  150. var _a = _this._options.minUptime, minUptime = _a === void 0 ? DEFAULT.minUptime : _a;
  151. clearTimeout(_this._connectTimeout);
  152. _this._uptimeTimeout = setTimeout(function () { return _this._acceptOpen(); }, minUptime);
  153. _this._ws.binaryType = _this._binaryType;
  154. // send enqueued messages (messages sent before websocket open event)
  155. _this._messageQueue.forEach(function (message) { return _this._ws.send(message); });
  156. _this._messageQueue = [];
  157. if (_this.onopen) {
  158. _this.onopen(event);
  159. }
  160. _this._listeners.open.forEach(function (listener) { return _this._callEventListener(event, listener); });
  161. };
  162. this._handleMessage = function (event) {
  163. _this._debug('message event');
  164. if (_this.onmessage) {
  165. _this.onmessage(event);
  166. }
  167. _this._listeners.message.forEach(function (listener) { return _this._callEventListener(event, listener); });
  168. };
  169. this._handleError = function (event) {
  170. _this._debug('error event', event.message);
  171. _this._disconnect(undefined, event.message === 'TIMEOUT' ? 'timeout' : undefined);
  172. if (_this.onerror) {
  173. _this.onerror(event);
  174. }
  175. _this._debug('exec error listeners');
  176. _this._listeners.error.forEach(function (listener) { return _this._callEventListener(event, listener); });
  177. _this._connect();
  178. };
  179. this._handleClose = function (event) {
  180. _this._debug('close event');
  181. _this._clearTimeouts();
  182. if (_this._shouldReconnect) {
  183. _this._connect();
  184. }
  185. if (_this.onclose) {
  186. _this.onclose(event);
  187. }
  188. _this._listeners.close.forEach(function (listener) { return _this._callEventListener(event, listener); });
  189. };
  190. this._url = url;
  191. this._protocols = protocols;
  192. this._options = options;
  193. if (this._options.startClosed) {
  194. this._shouldReconnect = false;
  195. }
  196. this._connect();
  197. }
  198. Object.defineProperty(ReconnectingWebSocket, "CONNECTING", {
  199. get: function () {
  200. return 0;
  201. },
  202. enumerable: true,
  203. configurable: true
  204. });
  205. Object.defineProperty(ReconnectingWebSocket, "OPEN", {
  206. get: function () {
  207. return 1;
  208. },
  209. enumerable: true,
  210. configurable: true
  211. });
  212. Object.defineProperty(ReconnectingWebSocket, "CLOSING", {
  213. get: function () {
  214. return 2;
  215. },
  216. enumerable: true,
  217. configurable: true
  218. });
  219. Object.defineProperty(ReconnectingWebSocket, "CLOSED", {
  220. get: function () {
  221. return 3;
  222. },
  223. enumerable: true,
  224. configurable: true
  225. });
  226. Object.defineProperty(ReconnectingWebSocket.prototype, "CONNECTING", {
  227. get: function () {
  228. return ReconnectingWebSocket.CONNECTING;
  229. },
  230. enumerable: true,
  231. configurable: true
  232. });
  233. Object.defineProperty(ReconnectingWebSocket.prototype, "OPEN", {
  234. get: function () {
  235. return ReconnectingWebSocket.OPEN;
  236. },
  237. enumerable: true,
  238. configurable: true
  239. });
  240. Object.defineProperty(ReconnectingWebSocket.prototype, "CLOSING", {
  241. get: function () {
  242. return ReconnectingWebSocket.CLOSING;
  243. },
  244. enumerable: true,
  245. configurable: true
  246. });
  247. Object.defineProperty(ReconnectingWebSocket.prototype, "CLOSED", {
  248. get: function () {
  249. return ReconnectingWebSocket.CLOSED;
  250. },
  251. enumerable: true,
  252. configurable: true
  253. });
  254. Object.defineProperty(ReconnectingWebSocket.prototype, "binaryType", {
  255. get: function () {
  256. return this._ws ? this._ws.binaryType : this._binaryType;
  257. },
  258. set: function (value) {
  259. this._binaryType = value;
  260. if (this._ws) {
  261. this._ws.binaryType = value;
  262. }
  263. },
  264. enumerable: true,
  265. configurable: true
  266. });
  267. Object.defineProperty(ReconnectingWebSocket.prototype, "retryCount", {
  268. /**
  269. * Returns the number or connection retries
  270. */
  271. get: function () {
  272. return Math.max(this._retryCount, 0);
  273. },
  274. enumerable: true,
  275. configurable: true
  276. });
  277. Object.defineProperty(ReconnectingWebSocket.prototype, "bufferedAmount", {
  278. /**
  279. * The number of bytes of data that have been queued using calls to send() but not yet
  280. * transmitted to the network. This value resets to zero once all queued data has been sent.
  281. * This value does not reset to zero when the connection is closed; if you keep calling send(),
  282. * this will continue to climb. Read only
  283. */
  284. get: function () {
  285. var bytes = this._messageQueue.reduce(function (acc, message) {
  286. if (typeof message === 'string') {
  287. acc += message.length; // not byte size
  288. }
  289. else if (message instanceof Blob) {
  290. acc += message.size;
  291. }
  292. else {
  293. acc += message.byteLength;
  294. }
  295. return acc;
  296. }, 0);
  297. return bytes + (this._ws ? this._ws.bufferedAmount : 0);
  298. },
  299. enumerable: true,
  300. configurable: true
  301. });
  302. Object.defineProperty(ReconnectingWebSocket.prototype, "extensions", {
  303. /**
  304. * The extensions selected by the server. This is currently only the empty string or a list of
  305. * extensions as negotiated by the connection
  306. */
  307. get: function () {
  308. return this._ws ? this._ws.extensions : '';
  309. },
  310. enumerable: true,
  311. configurable: true
  312. });
  313. Object.defineProperty(ReconnectingWebSocket.prototype, "protocol", {
  314. /**
  315. * A string indicating the name of the sub-protocol the server selected;
  316. * this will be one of the strings specified in the protocols parameter when creating the
  317. * WebSocket object
  318. */
  319. get: function () {
  320. return this._ws ? this._ws.protocol : '';
  321. },
  322. enumerable: true,
  323. configurable: true
  324. });
  325. Object.defineProperty(ReconnectingWebSocket.prototype, "readyState", {
  326. /**
  327. * The current state of the connection; this is one of the Ready state constants
  328. */
  329. get: function () {
  330. if (this._ws) {
  331. return this._ws.readyState;
  332. }
  333. return this._options.startClosed
  334. ? ReconnectingWebSocket.CLOSED
  335. : ReconnectingWebSocket.CONNECTING;
  336. },
  337. enumerable: true,
  338. configurable: true
  339. });
  340. Object.defineProperty(ReconnectingWebSocket.prototype, "url", {
  341. /**
  342. * The URL as resolved by the constructor
  343. */
  344. get: function () {
  345. return this._ws ? this._ws.url : '';
  346. },
  347. enumerable: true,
  348. configurable: true
  349. });
  350. /**
  351. * Closes the WebSocket connection or connection attempt, if any. If the connection is already
  352. * CLOSED, this method does nothing
  353. */
  354. ReconnectingWebSocket.prototype.close = function (code, reason) {
  355. if (code === void 0) { code = 1000; }
  356. this._closeCalled = true;
  357. this._shouldReconnect = false;
  358. this._clearTimeouts();
  359. if (!this._ws) {
  360. this._debug('close enqueued: no ws instance');
  361. return;
  362. }
  363. if (this._ws.readyState === this.CLOSED) {
  364. this._debug('close: already closed');
  365. return;
  366. }
  367. this._ws.close(code, reason);
  368. };
  369. /**
  370. * Closes the WebSocket connection or connection attempt and connects again.
  371. * Resets retry counter;
  372. */
  373. ReconnectingWebSocket.prototype.reconnect = function (code, reason) {
  374. this._shouldReconnect = true;
  375. this._closeCalled = false;
  376. this._retryCount = -1;
  377. if (!this._ws || this._ws.readyState === this.CLOSED) {
  378. this._connect();
  379. }
  380. else {
  381. this._disconnect(code, reason);
  382. this._connect();
  383. }
  384. };
  385. /**
  386. * Enqueue specified data to be transmitted to the server over the WebSocket connection
  387. */
  388. ReconnectingWebSocket.prototype.send = function (data) {
  389. if (this._ws && this._ws.readyState === this.OPEN) {
  390. this._debug('send', data);
  391. this._ws.send(data);
  392. }
  393. else {
  394. var _a = this._options.maxEnqueuedMessages, maxEnqueuedMessages = _a === void 0 ? DEFAULT.maxEnqueuedMessages : _a;
  395. if (this._messageQueue.length < maxEnqueuedMessages) {
  396. this._debug('enqueue', data);
  397. this._messageQueue.push(data);
  398. }
  399. }
  400. };
  401. /**
  402. * Register an event handler of a specific event type
  403. */
  404. ReconnectingWebSocket.prototype.addEventListener = function (type, listener) {
  405. if (this._listeners[type]) {
  406. // @ts-ignore
  407. this._listeners[type].push(listener);
  408. }
  409. };
  410. ReconnectingWebSocket.prototype.dispatchEvent = function (event) {
  411. var e_1, _a;
  412. var listeners = this._listeners[event.type];
  413. if (listeners) {
  414. try {
  415. for (var listeners_1 = __values(listeners), listeners_1_1 = listeners_1.next(); !listeners_1_1.done; listeners_1_1 = listeners_1.next()) {
  416. var listener = listeners_1_1.value;
  417. this._callEventListener(event, listener);
  418. }
  419. }
  420. catch (e_1_1) { e_1 = { error: e_1_1 }; }
  421. finally {
  422. try {
  423. if (listeners_1_1 && !listeners_1_1.done && (_a = listeners_1.return)) _a.call(listeners_1);
  424. }
  425. finally { if (e_1) throw e_1.error; }
  426. }
  427. }
  428. return true;
  429. };
  430. /**
  431. * Removes an event listener
  432. */
  433. ReconnectingWebSocket.prototype.removeEventListener = function (type, listener) {
  434. if (this._listeners[type]) {
  435. // @ts-ignore
  436. this._listeners[type] = this._listeners[type].filter(function (l) { return l !== listener; });
  437. }
  438. };
  439. ReconnectingWebSocket.prototype._debug = function () {
  440. var args = [];
  441. for (var _i = 0; _i < arguments.length; _i++) {
  442. args[_i] = arguments[_i];
  443. }
  444. if (this._options.debug) {
  445. // not using spread because compiled version uses Symbols
  446. // tslint:disable-next-line
  447. console.log.apply(console, __spread(['RWS>'], args));
  448. }
  449. };
  450. ReconnectingWebSocket.prototype._getNextDelay = function () {
  451. var _a = this._options, _b = _a.reconnectionDelayGrowFactor, reconnectionDelayGrowFactor = _b === void 0 ? DEFAULT.reconnectionDelayGrowFactor : _b, _c = _a.minReconnectionDelay, minReconnectionDelay = _c === void 0 ? DEFAULT.minReconnectionDelay : _c, _d = _a.maxReconnectionDelay, maxReconnectionDelay = _d === void 0 ? DEFAULT.maxReconnectionDelay : _d;
  452. var delay = 0;
  453. if (this._retryCount > 0) {
  454. delay =
  455. minReconnectionDelay * Math.pow(reconnectionDelayGrowFactor, this._retryCount - 1);
  456. if (delay > maxReconnectionDelay) {
  457. delay = maxReconnectionDelay;
  458. }
  459. }
  460. this._debug('next delay', delay);
  461. return delay;
  462. };
  463. ReconnectingWebSocket.prototype._wait = function () {
  464. var _this = this;
  465. return new Promise(function (resolve) {
  466. setTimeout(resolve, _this._getNextDelay());
  467. });
  468. };
  469. ReconnectingWebSocket.prototype._getNextUrl = function (urlProvider) {
  470. if (typeof urlProvider === 'string') {
  471. return Promise.resolve(urlProvider);
  472. }
  473. if (typeof urlProvider === 'function') {
  474. var url = urlProvider();
  475. if (typeof url === 'string') {
  476. return Promise.resolve(url);
  477. }
  478. if (!!url.then) {
  479. return url;
  480. }
  481. }
  482. throw Error('Invalid URL');
  483. };
  484. ReconnectingWebSocket.prototype._connect = function () {
  485. var _this = this;
  486. if (this._connectLock || !this._shouldReconnect) {
  487. return;
  488. }
  489. this._connectLock = true;
  490. var _a = this._options, _b = _a.maxRetries, maxRetries = _b === void 0 ? DEFAULT.maxRetries : _b, _c = _a.connectionTimeout, connectionTimeout = _c === void 0 ? DEFAULT.connectionTimeout : _c, _d = _a.WebSocket, WebSocket = _d === void 0 ? getGlobalWebSocket() : _d;
  491. if (this._retryCount >= maxRetries) {
  492. this._debug('max retries reached', this._retryCount, '>=', maxRetries);
  493. return;
  494. }
  495. this._retryCount++;
  496. this._debug('connect', this._retryCount);
  497. this._removeListeners();
  498. if (!isWebSocket(WebSocket)) {
  499. throw Error('No valid WebSocket class provided');
  500. }
  501. this._wait()
  502. .then(function () { return _this._getNextUrl(_this._url); })
  503. .then(function (url) {
  504. // close could be called before creating the ws
  505. if (_this._closeCalled) {
  506. return;
  507. }
  508. _this._debug('connect', { url: url, protocols: _this._protocols });
  509. _this._ws = _this._protocols
  510. ? new WebSocket(url, _this._protocols)
  511. : new WebSocket(url);
  512. _this._ws.binaryType = _this._binaryType;
  513. _this._connectLock = false;
  514. _this._addListeners();
  515. _this._connectTimeout = setTimeout(function () { return _this._handleTimeout(); }, connectionTimeout);
  516. });
  517. };
  518. ReconnectingWebSocket.prototype._handleTimeout = function () {
  519. this._debug('timeout event');
  520. this._handleError(new ErrorEvent(Error('TIMEOUT'), this));
  521. };
  522. ReconnectingWebSocket.prototype._disconnect = function (code, reason) {
  523. if (code === void 0) { code = 1000; }
  524. this._clearTimeouts();
  525. if (!this._ws) {
  526. return;
  527. }
  528. this._removeListeners();
  529. try {
  530. this._ws.close(code, reason);
  531. this._handleClose(new CloseEvent(code, reason, this));
  532. }
  533. catch (error) {
  534. // ignore
  535. }
  536. };
  537. ReconnectingWebSocket.prototype._acceptOpen = function () {
  538. this._debug('accept open');
  539. this._retryCount = 0;
  540. };
  541. ReconnectingWebSocket.prototype._callEventListener = function (event, listener) {
  542. if ('handleEvent' in listener) {
  543. // @ts-ignore
  544. listener.handleEvent(event);
  545. }
  546. else {
  547. // @ts-ignore
  548. listener(event);
  549. }
  550. };
  551. ReconnectingWebSocket.prototype._removeListeners = function () {
  552. if (!this._ws) {
  553. return;
  554. }
  555. this._debug('removeListeners');
  556. this._ws.removeEventListener('open', this._handleOpen);
  557. this._ws.removeEventListener('close', this._handleClose);
  558. this._ws.removeEventListener('message', this._handleMessage);
  559. // @ts-ignore
  560. this._ws.removeEventListener('error', this._handleError);
  561. };
  562. ReconnectingWebSocket.prototype._addListeners = function () {
  563. if (!this._ws) {
  564. return;
  565. }
  566. this._debug('addListeners');
  567. this._ws.addEventListener('open', this._handleOpen);
  568. this._ws.addEventListener('close', this._handleClose);
  569. this._ws.addEventListener('message', this._handleMessage);
  570. // @ts-ignore
  571. this._ws.addEventListener('error', this._handleError);
  572. };
  573. ReconnectingWebSocket.prototype._clearTimeouts = function () {
  574. clearTimeout(this._connectTimeout);
  575. clearTimeout(this._uptimeTimeout);
  576. };
  577. return ReconnectingWebSocket;
  578. }());
  579. return ReconnectingWebSocket;
  580. }());