websocket.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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.WS = void 0;
  7. const transport_js_1 = require("../transport.js");
  8. const yeast_js_1 = require("../contrib/yeast.js");
  9. const util_js_1 = require("../util.js");
  10. const websocket_constructor_js_1 = require("./websocket-constructor.js");
  11. const debug_1 = __importDefault(require("debug")); // debug()
  12. const engine_io_parser_1 = require("engine.io-parser");
  13. const debug = (0, debug_1.default)("engine.io-client:websocket"); // debug()
  14. // detect ReactNative environment
  15. const isReactNative = typeof navigator !== "undefined" &&
  16. typeof navigator.product === "string" &&
  17. navigator.product.toLowerCase() === "reactnative";
  18. class WS extends transport_js_1.Transport {
  19. /**
  20. * WebSocket transport constructor.
  21. *
  22. * @param {Object} opts - connection options
  23. * @protected
  24. */
  25. constructor(opts) {
  26. super(opts);
  27. this.supportsBinary = !opts.forceBase64;
  28. }
  29. get name() {
  30. return "websocket";
  31. }
  32. doOpen() {
  33. if (!this.check()) {
  34. // let probe timeout
  35. return;
  36. }
  37. const uri = this.uri();
  38. const protocols = this.opts.protocols;
  39. // React Native only supports the 'headers' option, and will print a warning if anything else is passed
  40. const opts = isReactNative
  41. ? {}
  42. : (0, util_js_1.pick)(this.opts, "agent", "perMessageDeflate", "pfx", "key", "passphrase", "cert", "ca", "ciphers", "rejectUnauthorized", "localAddress", "protocolVersion", "origin", "maxPayload", "family", "checkServerIdentity");
  43. if (this.opts.extraHeaders) {
  44. opts.headers = this.opts.extraHeaders;
  45. }
  46. try {
  47. this.ws =
  48. websocket_constructor_js_1.usingBrowserWebSocket && !isReactNative
  49. ? protocols
  50. ? new websocket_constructor_js_1.WebSocket(uri, protocols)
  51. : new websocket_constructor_js_1.WebSocket(uri)
  52. : new websocket_constructor_js_1.WebSocket(uri, protocols, opts);
  53. }
  54. catch (err) {
  55. return this.emitReserved("error", err);
  56. }
  57. this.ws.binaryType = this.socket.binaryType;
  58. this.addEventListeners();
  59. }
  60. /**
  61. * Adds event listeners to the socket
  62. *
  63. * @private
  64. */
  65. addEventListeners() {
  66. this.ws.onopen = () => {
  67. if (this.opts.autoUnref) {
  68. this.ws._socket.unref();
  69. }
  70. this.onOpen();
  71. };
  72. this.ws.onclose = (closeEvent) => this.onClose({
  73. description: "websocket connection closed",
  74. context: closeEvent,
  75. });
  76. this.ws.onmessage = (ev) => this.onData(ev.data);
  77. this.ws.onerror = (e) => this.onError("websocket error", e);
  78. }
  79. write(packets) {
  80. this.writable = false;
  81. // encodePacket efficient as it uses WS framing
  82. // no need for encodePayload
  83. for (let i = 0; i < packets.length; i++) {
  84. const packet = packets[i];
  85. const lastPacket = i === packets.length - 1;
  86. (0, engine_io_parser_1.encodePacket)(packet, this.supportsBinary, (data) => {
  87. // always create a new object (GH-437)
  88. const opts = {};
  89. if (!websocket_constructor_js_1.usingBrowserWebSocket) {
  90. if (packet.options) {
  91. opts.compress = packet.options.compress;
  92. }
  93. if (this.opts.perMessageDeflate) {
  94. const len =
  95. // @ts-ignore
  96. "string" === typeof data ? Buffer.byteLength(data) : data.length;
  97. if (len < this.opts.perMessageDeflate.threshold) {
  98. opts.compress = false;
  99. }
  100. }
  101. }
  102. // Sometimes the websocket has already been closed but the browser didn't
  103. // have a chance of informing us about it yet, in that case send will
  104. // throw an error
  105. try {
  106. if (websocket_constructor_js_1.usingBrowserWebSocket) {
  107. // TypeError is thrown when passing the second argument on Safari
  108. this.ws.send(data);
  109. }
  110. else {
  111. this.ws.send(data, opts);
  112. }
  113. }
  114. catch (e) {
  115. debug("websocket closed before onclose event");
  116. }
  117. if (lastPacket) {
  118. // fake drain
  119. // defer to next tick to allow Socket to clear writeBuffer
  120. (0, websocket_constructor_js_1.nextTick)(() => {
  121. this.writable = true;
  122. this.emitReserved("drain");
  123. }, this.setTimeoutFn);
  124. }
  125. });
  126. }
  127. }
  128. doClose() {
  129. if (typeof this.ws !== "undefined") {
  130. this.ws.close();
  131. this.ws = null;
  132. }
  133. }
  134. /**
  135. * Generates uri for connection.
  136. *
  137. * @private
  138. */
  139. uri() {
  140. const schema = this.opts.secure ? "wss" : "ws";
  141. const query = this.query || {};
  142. // append timestamp to URI
  143. if (this.opts.timestampRequests) {
  144. query[this.opts.timestampParam] = (0, yeast_js_1.yeast)();
  145. }
  146. // communicate binary support capabilities
  147. if (!this.supportsBinary) {
  148. query.b64 = 1;
  149. }
  150. return this.createUri(schema, query);
  151. }
  152. /**
  153. * Feature detection for WebSocket.
  154. *
  155. * @return {Boolean} whether this transport is available.
  156. * @private
  157. */
  158. check() {
  159. return !!websocket_constructor_js_1.WebSocket;
  160. }
  161. }
  162. exports.WS = WS;