uniapp,h5

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766
  1. "use strict";
  2. var __importDefault = (this && this.__importDefault) || function (mod) {
  3. return (mod && mod.__esModule) ? mod : { "default": mod };
  4. };
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. exports.Socket = exports.SocketWithUpgrade = exports.SocketWithoutUpgrade = void 0;
  7. const index_js_1 = require("./transports/index.js");
  8. const util_js_1 = require("./util.js");
  9. const parseqs_js_1 = require("./contrib/parseqs.js");
  10. const parseuri_js_1 = require("./contrib/parseuri.js");
  11. const component_emitter_1 = require("@socket.io/component-emitter");
  12. const engine_io_parser_1 = require("engine.io-parser");
  13. const globals_node_js_1 = require("./globals.node.js");
  14. const debug_1 = __importDefault(require("debug")); // debug()
  15. const debug = (0, debug_1.default)("engine.io-client:socket"); // debug()
  16. const withEventListeners = typeof addEventListener === "function" &&
  17. typeof removeEventListener === "function";
  18. const OFFLINE_EVENT_LISTENERS = [];
  19. if (withEventListeners) {
  20. // within a ServiceWorker, any event handler for the 'offline' event must be added on the initial evaluation of the
  21. // script, so we create one single event listener here which will forward the event to the socket instances
  22. addEventListener("offline", () => {
  23. debug("closing %d connection(s) because the network was lost", OFFLINE_EVENT_LISTENERS.length);
  24. OFFLINE_EVENT_LISTENERS.forEach((listener) => listener());
  25. }, false);
  26. }
  27. /**
  28. * This class provides a WebSocket-like interface to connect to an Engine.IO server. The connection will be established
  29. * with one of the available low-level transports, like HTTP long-polling, WebSocket or WebTransport.
  30. *
  31. * This class comes without upgrade mechanism, which means that it will keep the first low-level transport that
  32. * successfully establishes the connection.
  33. *
  34. * In order to allow tree-shaking, there are no transports included, that's why the `transports` option is mandatory.
  35. *
  36. * @example
  37. * import { SocketWithoutUpgrade, WebSocket } from "engine.io-client";
  38. *
  39. * const socket = new SocketWithoutUpgrade({
  40. * transports: [WebSocket]
  41. * });
  42. *
  43. * socket.on("open", () => {
  44. * socket.send("hello");
  45. * });
  46. *
  47. * @see SocketWithUpgrade
  48. * @see Socket
  49. */
  50. class SocketWithoutUpgrade extends component_emitter_1.Emitter {
  51. /**
  52. * Socket constructor.
  53. *
  54. * @param {String|Object} uri - uri or options
  55. * @param {Object} opts - options
  56. */
  57. constructor(uri, opts) {
  58. super();
  59. this.binaryType = globals_node_js_1.defaultBinaryType;
  60. this.writeBuffer = [];
  61. this._prevBufferLen = 0;
  62. this._pingInterval = -1;
  63. this._pingTimeout = -1;
  64. this._maxPayload = -1;
  65. /**
  66. * The expiration timestamp of the {@link _pingTimeoutTimer} object is tracked, in case the timer is throttled and the
  67. * callback is not fired on time. This can happen for example when a laptop is suspended or when a phone is locked.
  68. */
  69. this._pingTimeoutTime = Infinity;
  70. if (uri && "object" === typeof uri) {
  71. opts = uri;
  72. uri = null;
  73. }
  74. if (uri) {
  75. const parsedUri = (0, parseuri_js_1.parse)(uri);
  76. opts.hostname = parsedUri.host;
  77. opts.secure =
  78. parsedUri.protocol === "https" || parsedUri.protocol === "wss";
  79. opts.port = parsedUri.port;
  80. if (parsedUri.query)
  81. opts.query = parsedUri.query;
  82. }
  83. else if (opts.host) {
  84. opts.hostname = (0, parseuri_js_1.parse)(opts.host).host;
  85. }
  86. (0, util_js_1.installTimerFunctions)(this, opts);
  87. this.secure =
  88. null != opts.secure
  89. ? opts.secure
  90. : typeof location !== "undefined" && "https:" === location.protocol;
  91. if (opts.hostname && !opts.port) {
  92. // if no port is specified manually, use the protocol default
  93. opts.port = this.secure ? "443" : "80";
  94. }
  95. this.hostname =
  96. opts.hostname ||
  97. (typeof location !== "undefined" ? location.hostname : "localhost");
  98. this.port =
  99. opts.port ||
  100. (typeof location !== "undefined" && location.port
  101. ? location.port
  102. : this.secure
  103. ? "443"
  104. : "80");
  105. this.transports = [];
  106. this._transportsByName = {};
  107. opts.transports.forEach((t) => {
  108. const transportName = t.prototype.name;
  109. this.transports.push(transportName);
  110. this._transportsByName[transportName] = t;
  111. });
  112. this.opts = Object.assign({
  113. path: "/engine.io",
  114. agent: false,
  115. withCredentials: false,
  116. upgrade: true,
  117. timestampParam: "t",
  118. rememberUpgrade: false,
  119. addTrailingSlash: true,
  120. rejectUnauthorized: true,
  121. perMessageDeflate: {
  122. threshold: 1024,
  123. },
  124. transportOptions: {},
  125. closeOnBeforeunload: false,
  126. }, opts);
  127. this.opts.path =
  128. this.opts.path.replace(/\/$/, "") +
  129. (this.opts.addTrailingSlash ? "/" : "");
  130. if (typeof this.opts.query === "string") {
  131. this.opts.query = (0, parseqs_js_1.decode)(this.opts.query);
  132. }
  133. if (withEventListeners) {
  134. if (this.opts.closeOnBeforeunload) {
  135. // Firefox closes the connection when the "beforeunload" event is emitted but not Chrome. This event listener
  136. // ensures every browser behaves the same (no "disconnect" event at the Socket.IO level when the page is
  137. // closed/reloaded)
  138. this._beforeunloadEventListener = () => {
  139. if (this.transport) {
  140. // silently close the transport
  141. this.transport.removeAllListeners();
  142. this.transport.close();
  143. }
  144. };
  145. addEventListener("beforeunload", this._beforeunloadEventListener, false);
  146. }
  147. if (this.hostname !== "localhost") {
  148. debug("adding listener for the 'offline' event");
  149. this._offlineEventListener = () => {
  150. this._onClose("transport close", {
  151. description: "network connection lost",
  152. });
  153. };
  154. OFFLINE_EVENT_LISTENERS.push(this._offlineEventListener);
  155. }
  156. }
  157. if (this.opts.withCredentials) {
  158. this._cookieJar = (0, globals_node_js_1.createCookieJar)();
  159. }
  160. this._open();
  161. }
  162. /**
  163. * Creates transport of the given type.
  164. *
  165. * @param {String} name - transport name
  166. * @return {Transport}
  167. * @private
  168. */
  169. createTransport(name) {
  170. debug('creating transport "%s"', name);
  171. const query = Object.assign({}, this.opts.query);
  172. // append engine.io protocol identifier
  173. query.EIO = engine_io_parser_1.protocol;
  174. // transport name
  175. query.transport = name;
  176. // session id if we already have one
  177. if (this.id)
  178. query.sid = this.id;
  179. const opts = Object.assign({}, this.opts, {
  180. query,
  181. socket: this,
  182. hostname: this.hostname,
  183. secure: this.secure,
  184. port: this.port,
  185. }, this.opts.transportOptions[name]);
  186. debug("options: %j", opts);
  187. return new this._transportsByName[name](opts);
  188. }
  189. /**
  190. * Initializes transport to use and starts probe.
  191. *
  192. * @private
  193. */
  194. _open() {
  195. if (this.transports.length === 0) {
  196. // Emit error on next tick so it can be listened to
  197. this.setTimeoutFn(() => {
  198. this.emitReserved("error", "No transports available");
  199. }, 0);
  200. return;
  201. }
  202. const transportName = this.opts.rememberUpgrade &&
  203. SocketWithoutUpgrade.priorWebsocketSuccess &&
  204. this.transports.indexOf("websocket") !== -1
  205. ? "websocket"
  206. : this.transports[0];
  207. this.readyState = "opening";
  208. const transport = this.createTransport(transportName);
  209. transport.open();
  210. this.setTransport(transport);
  211. }
  212. /**
  213. * Sets the current transport. Disables the existing one (if any).
  214. *
  215. * @private
  216. */
  217. setTransport(transport) {
  218. debug("setting transport %s", transport.name);
  219. if (this.transport) {
  220. debug("clearing existing transport %s", this.transport.name);
  221. this.transport.removeAllListeners();
  222. }
  223. // set up transport
  224. this.transport = transport;
  225. // set up transport listeners
  226. transport
  227. .on("drain", this._onDrain.bind(this))
  228. .on("packet", this._onPacket.bind(this))
  229. .on("error", this._onError.bind(this))
  230. .on("close", (reason) => this._onClose("transport close", reason));
  231. }
  232. /**
  233. * Called when connection is deemed open.
  234. *
  235. * @private
  236. */
  237. onOpen() {
  238. debug("socket open");
  239. this.readyState = "open";
  240. SocketWithoutUpgrade.priorWebsocketSuccess =
  241. "websocket" === this.transport.name;
  242. this.emitReserved("open");
  243. this.flush();
  244. }
  245. /**
  246. * Handles a packet.
  247. *
  248. * @private
  249. */
  250. _onPacket(packet) {
  251. if ("opening" === this.readyState ||
  252. "open" === this.readyState ||
  253. "closing" === this.readyState) {
  254. debug('socket receive: type "%s", data "%s"', packet.type, packet.data);
  255. this.emitReserved("packet", packet);
  256. // Socket is live - any packet counts
  257. this.emitReserved("heartbeat");
  258. switch (packet.type) {
  259. case "open":
  260. this.onHandshake(JSON.parse(packet.data));
  261. break;
  262. case "ping":
  263. this._sendPacket("pong");
  264. this.emitReserved("ping");
  265. this.emitReserved("pong");
  266. this._resetPingTimeout();
  267. break;
  268. case "error":
  269. const err = new Error("server error");
  270. // @ts-ignore
  271. err.code = packet.data;
  272. this._onError(err);
  273. break;
  274. case "message":
  275. this.emitReserved("data", packet.data);
  276. this.emitReserved("message", packet.data);
  277. break;
  278. }
  279. }
  280. else {
  281. debug('packet received with socket readyState "%s"', this.readyState);
  282. }
  283. }
  284. /**
  285. * Called upon handshake completion.
  286. *
  287. * @param {Object} data - handshake obj
  288. * @private
  289. */
  290. onHandshake(data) {
  291. this.emitReserved("handshake", data);
  292. this.id = data.sid;
  293. this.transport.query.sid = data.sid;
  294. this._pingInterval = data.pingInterval;
  295. this._pingTimeout = data.pingTimeout;
  296. this._maxPayload = data.maxPayload;
  297. this.onOpen();
  298. // In case open handler closes socket
  299. if ("closed" === this.readyState)
  300. return;
  301. this._resetPingTimeout();
  302. }
  303. /**
  304. * Sets and resets ping timeout timer based on server pings.
  305. *
  306. * @private
  307. */
  308. _resetPingTimeout() {
  309. this.clearTimeoutFn(this._pingTimeoutTimer);
  310. const delay = this._pingInterval + this._pingTimeout;
  311. this._pingTimeoutTime = Date.now() + delay;
  312. this._pingTimeoutTimer = this.setTimeoutFn(() => {
  313. this._onClose("ping timeout");
  314. }, delay);
  315. if (this.opts.autoUnref) {
  316. this._pingTimeoutTimer.unref();
  317. }
  318. }
  319. /**
  320. * Called on `drain` event
  321. *
  322. * @private
  323. */
  324. _onDrain() {
  325. this.writeBuffer.splice(0, this._prevBufferLen);
  326. // setting prevBufferLen = 0 is very important
  327. // for example, when upgrading, upgrade packet is sent over,
  328. // and a nonzero prevBufferLen could cause problems on `drain`
  329. this._prevBufferLen = 0;
  330. if (0 === this.writeBuffer.length) {
  331. this.emitReserved("drain");
  332. }
  333. else {
  334. this.flush();
  335. }
  336. }
  337. /**
  338. * Flush write buffers.
  339. *
  340. * @private
  341. */
  342. flush() {
  343. if ("closed" !== this.readyState &&
  344. this.transport.writable &&
  345. !this.upgrading &&
  346. this.writeBuffer.length) {
  347. const packets = this._getWritablePackets();
  348. debug("flushing %d packets in socket", packets.length);
  349. this.transport.send(packets);
  350. // keep track of current length of writeBuffer
  351. // splice writeBuffer and callbackBuffer on `drain`
  352. this._prevBufferLen = packets.length;
  353. this.emitReserved("flush");
  354. }
  355. }
  356. /**
  357. * Ensure the encoded size of the writeBuffer is below the maxPayload value sent by the server (only for HTTP
  358. * long-polling)
  359. *
  360. * @private
  361. */
  362. _getWritablePackets() {
  363. const shouldCheckPayloadSize = this._maxPayload &&
  364. this.transport.name === "polling" &&
  365. this.writeBuffer.length > 1;
  366. if (!shouldCheckPayloadSize) {
  367. return this.writeBuffer;
  368. }
  369. let payloadSize = 1; // first packet type
  370. for (let i = 0; i < this.writeBuffer.length; i++) {
  371. const data = this.writeBuffer[i].data;
  372. if (data) {
  373. payloadSize += (0, util_js_1.byteLength)(data);
  374. }
  375. if (i > 0 && payloadSize > this._maxPayload) {
  376. debug("only send %d out of %d packets", i, this.writeBuffer.length);
  377. return this.writeBuffer.slice(0, i);
  378. }
  379. payloadSize += 2; // separator + packet type
  380. }
  381. debug("payload size is %d (max: %d)", payloadSize, this._maxPayload);
  382. return this.writeBuffer;
  383. }
  384. /**
  385. * Checks whether the heartbeat timer has expired but the socket has not yet been notified.
  386. *
  387. * Note: this method is private for now because it does not really fit the WebSocket API, but if we put it in the
  388. * `write()` method then the message would not be buffered by the Socket.IO client.
  389. *
  390. * @return {boolean}
  391. * @private
  392. */
  393. /* private */ _hasPingExpired() {
  394. if (!this._pingTimeoutTime)
  395. return true;
  396. const hasExpired = Date.now() > this._pingTimeoutTime;
  397. if (hasExpired) {
  398. debug("throttled timer detected, scheduling connection close");
  399. this._pingTimeoutTime = 0;
  400. (0, globals_node_js_1.nextTick)(() => {
  401. this._onClose("ping timeout");
  402. }, this.setTimeoutFn);
  403. }
  404. return hasExpired;
  405. }
  406. /**
  407. * Sends a message.
  408. *
  409. * @param {String} msg - message.
  410. * @param {Object} options.
  411. * @param {Function} fn - callback function.
  412. * @return {Socket} for chaining.
  413. */
  414. write(msg, options, fn) {
  415. this._sendPacket("message", msg, options, fn);
  416. return this;
  417. }
  418. /**
  419. * Sends a message. Alias of {@link Socket#write}.
  420. *
  421. * @param {String} msg - message.
  422. * @param {Object} options.
  423. * @param {Function} fn - callback function.
  424. * @return {Socket} for chaining.
  425. */
  426. send(msg, options, fn) {
  427. this._sendPacket("message", msg, options, fn);
  428. return this;
  429. }
  430. /**
  431. * Sends a packet.
  432. *
  433. * @param {String} type: packet type.
  434. * @param {String} data.
  435. * @param {Object} options.
  436. * @param {Function} fn - callback function.
  437. * @private
  438. */
  439. _sendPacket(type, data, options, fn) {
  440. if ("function" === typeof data) {
  441. fn = data;
  442. data = undefined;
  443. }
  444. if ("function" === typeof options) {
  445. fn = options;
  446. options = null;
  447. }
  448. if ("closing" === this.readyState || "closed" === this.readyState) {
  449. return;
  450. }
  451. options = options || {};
  452. options.compress = false !== options.compress;
  453. const packet = {
  454. type: type,
  455. data: data,
  456. options: options,
  457. };
  458. this.emitReserved("packetCreate", packet);
  459. this.writeBuffer.push(packet);
  460. if (fn)
  461. this.once("flush", fn);
  462. this.flush();
  463. }
  464. /**
  465. * Closes the connection.
  466. */
  467. close() {
  468. const close = () => {
  469. this._onClose("forced close");
  470. debug("socket closing - telling transport to close");
  471. this.transport.close();
  472. };
  473. const cleanupAndClose = () => {
  474. this.off("upgrade", cleanupAndClose);
  475. this.off("upgradeError", cleanupAndClose);
  476. close();
  477. };
  478. const waitForUpgrade = () => {
  479. // wait for upgrade to finish since we can't send packets while pausing a transport
  480. this.once("upgrade", cleanupAndClose);
  481. this.once("upgradeError", cleanupAndClose);
  482. };
  483. if ("opening" === this.readyState || "open" === this.readyState) {
  484. this.readyState = "closing";
  485. if (this.writeBuffer.length) {
  486. this.once("drain", () => {
  487. if (this.upgrading) {
  488. waitForUpgrade();
  489. }
  490. else {
  491. close();
  492. }
  493. });
  494. }
  495. else if (this.upgrading) {
  496. waitForUpgrade();
  497. }
  498. else {
  499. close();
  500. }
  501. }
  502. return this;
  503. }
  504. /**
  505. * Called upon transport error
  506. *
  507. * @private
  508. */
  509. _onError(err) {
  510. debug("socket error %j", err);
  511. SocketWithoutUpgrade.priorWebsocketSuccess = false;
  512. if (this.opts.tryAllTransports &&
  513. this.transports.length > 1 &&
  514. this.readyState === "opening") {
  515. debug("trying next transport");
  516. this.transports.shift();
  517. return this._open();
  518. }
  519. this.emitReserved("error", err);
  520. this._onClose("transport error", err);
  521. }
  522. /**
  523. * Called upon transport close.
  524. *
  525. * @private
  526. */
  527. _onClose(reason, description) {
  528. if ("opening" === this.readyState ||
  529. "open" === this.readyState ||
  530. "closing" === this.readyState) {
  531. debug('socket close with reason: "%s"', reason);
  532. // clear timers
  533. this.clearTimeoutFn(this._pingTimeoutTimer);
  534. // stop event from firing again for transport
  535. this.transport.removeAllListeners("close");
  536. // ensure transport won't stay open
  537. this.transport.close();
  538. // ignore further transport communication
  539. this.transport.removeAllListeners();
  540. if (withEventListeners) {
  541. if (this._beforeunloadEventListener) {
  542. removeEventListener("beforeunload", this._beforeunloadEventListener, false);
  543. }
  544. if (this._offlineEventListener) {
  545. const i = OFFLINE_EVENT_LISTENERS.indexOf(this._offlineEventListener);
  546. if (i !== -1) {
  547. debug("removing listener for the 'offline' event");
  548. OFFLINE_EVENT_LISTENERS.splice(i, 1);
  549. }
  550. }
  551. }
  552. // set ready state
  553. this.readyState = "closed";
  554. // clear session id
  555. this.id = null;
  556. // emit close event
  557. this.emitReserved("close", reason, description);
  558. // clean buffers after, so users can still
  559. // grab the buffers on `close` event
  560. this.writeBuffer = [];
  561. this._prevBufferLen = 0;
  562. }
  563. }
  564. }
  565. exports.SocketWithoutUpgrade = SocketWithoutUpgrade;
  566. SocketWithoutUpgrade.protocol = engine_io_parser_1.protocol;
  567. /**
  568. * This class provides a WebSocket-like interface to connect to an Engine.IO server. The connection will be established
  569. * with one of the available low-level transports, like HTTP long-polling, WebSocket or WebTransport.
  570. *
  571. * This class comes with an upgrade mechanism, which means that once the connection is established with the first
  572. * low-level transport, it will try to upgrade to a better transport.
  573. *
  574. * In order to allow tree-shaking, there are no transports included, that's why the `transports` option is mandatory.
  575. *
  576. * @example
  577. * import { SocketWithUpgrade, WebSocket } from "engine.io-client";
  578. *
  579. * const socket = new SocketWithUpgrade({
  580. * transports: [WebSocket]
  581. * });
  582. *
  583. * socket.on("open", () => {
  584. * socket.send("hello");
  585. * });
  586. *
  587. * @see SocketWithoutUpgrade
  588. * @see Socket
  589. */
  590. class SocketWithUpgrade extends SocketWithoutUpgrade {
  591. constructor() {
  592. super(...arguments);
  593. this._upgrades = [];
  594. }
  595. onOpen() {
  596. super.onOpen();
  597. if ("open" === this.readyState && this.opts.upgrade) {
  598. debug("starting upgrade probes");
  599. for (let i = 0; i < this._upgrades.length; i++) {
  600. this._probe(this._upgrades[i]);
  601. }
  602. }
  603. }
  604. /**
  605. * Probes a transport.
  606. *
  607. * @param {String} name - transport name
  608. * @private
  609. */
  610. _probe(name) {
  611. debug('probing transport "%s"', name);
  612. let transport = this.createTransport(name);
  613. let failed = false;
  614. SocketWithoutUpgrade.priorWebsocketSuccess = false;
  615. const onTransportOpen = () => {
  616. if (failed)
  617. return;
  618. debug('probe transport "%s" opened', name);
  619. transport.send([{ type: "ping", data: "probe" }]);
  620. transport.once("packet", (msg) => {
  621. if (failed)
  622. return;
  623. if ("pong" === msg.type && "probe" === msg.data) {
  624. debug('probe transport "%s" pong', name);
  625. this.upgrading = true;
  626. this.emitReserved("upgrading", transport);
  627. if (!transport)
  628. return;
  629. SocketWithoutUpgrade.priorWebsocketSuccess =
  630. "websocket" === transport.name;
  631. debug('pausing current transport "%s"', this.transport.name);
  632. this.transport.pause(() => {
  633. if (failed)
  634. return;
  635. if ("closed" === this.readyState)
  636. return;
  637. debug("changing transport and sending upgrade packet");
  638. cleanup();
  639. this.setTransport(transport);
  640. transport.send([{ type: "upgrade" }]);
  641. this.emitReserved("upgrade", transport);
  642. transport = null;
  643. this.upgrading = false;
  644. this.flush();
  645. });
  646. }
  647. else {
  648. debug('probe transport "%s" failed', name);
  649. const err = new Error("probe error");
  650. // @ts-ignore
  651. err.transport = transport.name;
  652. this.emitReserved("upgradeError", err);
  653. }
  654. });
  655. };
  656. function freezeTransport() {
  657. if (failed)
  658. return;
  659. // Any callback called by transport should be ignored since now
  660. failed = true;
  661. cleanup();
  662. transport.close();
  663. transport = null;
  664. }
  665. // Handle any error that happens while probing
  666. const onerror = (err) => {
  667. const error = new Error("probe error: " + err);
  668. // @ts-ignore
  669. error.transport = transport.name;
  670. freezeTransport();
  671. debug('probe transport "%s" failed because of error: %s', name, err);
  672. this.emitReserved("upgradeError", error);
  673. };
  674. function onTransportClose() {
  675. onerror("transport closed");
  676. }
  677. // When the socket is closed while we're probing
  678. function onclose() {
  679. onerror("socket closed");
  680. }
  681. // When the socket is upgraded while we're probing
  682. function onupgrade(to) {
  683. if (transport && to.name !== transport.name) {
  684. debug('"%s" works - aborting "%s"', to.name, transport.name);
  685. freezeTransport();
  686. }
  687. }
  688. // Remove all listeners on the transport and on self
  689. const cleanup = () => {
  690. transport.removeListener("open", onTransportOpen);
  691. transport.removeListener("error", onerror);
  692. transport.removeListener("close", onTransportClose);
  693. this.off("close", onclose);
  694. this.off("upgrading", onupgrade);
  695. };
  696. transport.once("open", onTransportOpen);
  697. transport.once("error", onerror);
  698. transport.once("close", onTransportClose);
  699. this.once("close", onclose);
  700. this.once("upgrading", onupgrade);
  701. if (this._upgrades.indexOf("webtransport") !== -1 &&
  702. name !== "webtransport") {
  703. // favor WebTransport
  704. this.setTimeoutFn(() => {
  705. if (!failed) {
  706. transport.open();
  707. }
  708. }, 200);
  709. }
  710. else {
  711. transport.open();
  712. }
  713. }
  714. onHandshake(data) {
  715. this._upgrades = this._filterUpgrades(data.upgrades);
  716. super.onHandshake(data);
  717. }
  718. /**
  719. * Filters upgrades, returning only those matching client transports.
  720. *
  721. * @param {Array} upgrades - server upgrades
  722. * @private
  723. */
  724. _filterUpgrades(upgrades) {
  725. const filteredUpgrades = [];
  726. for (let i = 0; i < upgrades.length; i++) {
  727. if (~this.transports.indexOf(upgrades[i]))
  728. filteredUpgrades.push(upgrades[i]);
  729. }
  730. return filteredUpgrades;
  731. }
  732. }
  733. exports.SocketWithUpgrade = SocketWithUpgrade;
  734. /**
  735. * This class provides a WebSocket-like interface to connect to an Engine.IO server. The connection will be established
  736. * with one of the available low-level transports, like HTTP long-polling, WebSocket or WebTransport.
  737. *
  738. * This class comes with an upgrade mechanism, which means that once the connection is established with the first
  739. * low-level transport, it will try to upgrade to a better transport.
  740. *
  741. * @example
  742. * import { Socket } from "engine.io-client";
  743. *
  744. * const socket = new Socket();
  745. *
  746. * socket.on("open", () => {
  747. * socket.send("hello");
  748. * });
  749. *
  750. * @see SocketWithoutUpgrade
  751. * @see SocketWithUpgrade
  752. */
  753. class Socket extends SocketWithUpgrade {
  754. constructor(uri, opts = {}) {
  755. const o = typeof uri === "object" ? uri : opts;
  756. if (!o.transports ||
  757. (o.transports && typeof o.transports[0] === "string")) {
  758. o.transports = (o.transports || ["polling", "websocket", "webtransport"])
  759. .map((transportName) => index_js_1.transports[transportName])
  760. .filter((t) => !!t);
  761. }
  762. super(uri, o);
  763. }
  764. }
  765. exports.Socket = Socket;