implemented basic client-server communication
This commit is contained in:
		
							
								
								
									
										131
									
								
								node_modules/ws/lib/buffer-util.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								node_modules/ws/lib/buffer-util.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,131 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
const { EMPTY_BUFFER } = require('./constants');
 | 
			
		||||
 | 
			
		||||
const FastBuffer = Buffer[Symbol.species];
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Merges an array of buffers into a new buffer.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {Buffer[]} list The array of buffers to concat
 | 
			
		||||
 * @param {Number} totalLength The total length of buffers in the list
 | 
			
		||||
 * @return {Buffer} The resulting buffer
 | 
			
		||||
 * @public
 | 
			
		||||
 */
 | 
			
		||||
function concat(list, totalLength) {
 | 
			
		||||
  if (list.length === 0) return EMPTY_BUFFER;
 | 
			
		||||
  if (list.length === 1) return list[0];
 | 
			
		||||
 | 
			
		||||
  const target = Buffer.allocUnsafe(totalLength);
 | 
			
		||||
  let offset = 0;
 | 
			
		||||
 | 
			
		||||
  for (let i = 0; i < list.length; i++) {
 | 
			
		||||
    const buf = list[i];
 | 
			
		||||
    target.set(buf, offset);
 | 
			
		||||
    offset += buf.length;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (offset < totalLength) {
 | 
			
		||||
    return new FastBuffer(target.buffer, target.byteOffset, offset);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return target;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Masks a buffer using the given mask.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {Buffer} source The buffer to mask
 | 
			
		||||
 * @param {Buffer} mask The mask to use
 | 
			
		||||
 * @param {Buffer} output The buffer where to store the result
 | 
			
		||||
 * @param {Number} offset The offset at which to start writing
 | 
			
		||||
 * @param {Number} length The number of bytes to mask.
 | 
			
		||||
 * @public
 | 
			
		||||
 */
 | 
			
		||||
function _mask(source, mask, output, offset, length) {
 | 
			
		||||
  for (let i = 0; i < length; i++) {
 | 
			
		||||
    output[offset + i] = source[i] ^ mask[i & 3];
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Unmasks a buffer using the given mask.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {Buffer} buffer The buffer to unmask
 | 
			
		||||
 * @param {Buffer} mask The mask to use
 | 
			
		||||
 * @public
 | 
			
		||||
 */
 | 
			
		||||
function _unmask(buffer, mask) {
 | 
			
		||||
  for (let i = 0; i < buffer.length; i++) {
 | 
			
		||||
    buffer[i] ^= mask[i & 3];
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Converts a buffer to an `ArrayBuffer`.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {Buffer} buf The buffer to convert
 | 
			
		||||
 * @return {ArrayBuffer} Converted buffer
 | 
			
		||||
 * @public
 | 
			
		||||
 */
 | 
			
		||||
function toArrayBuffer(buf) {
 | 
			
		||||
  if (buf.length === buf.buffer.byteLength) {
 | 
			
		||||
    return buf.buffer;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.length);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Converts `data` to a `Buffer`.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {*} data The data to convert
 | 
			
		||||
 * @return {Buffer} The buffer
 | 
			
		||||
 * @throws {TypeError}
 | 
			
		||||
 * @public
 | 
			
		||||
 */
 | 
			
		||||
function toBuffer(data) {
 | 
			
		||||
  toBuffer.readOnly = true;
 | 
			
		||||
 | 
			
		||||
  if (Buffer.isBuffer(data)) return data;
 | 
			
		||||
 | 
			
		||||
  let buf;
 | 
			
		||||
 | 
			
		||||
  if (data instanceof ArrayBuffer) {
 | 
			
		||||
    buf = new FastBuffer(data);
 | 
			
		||||
  } else if (ArrayBuffer.isView(data)) {
 | 
			
		||||
    buf = new FastBuffer(data.buffer, data.byteOffset, data.byteLength);
 | 
			
		||||
  } else {
 | 
			
		||||
    buf = Buffer.from(data);
 | 
			
		||||
    toBuffer.readOnly = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
  concat,
 | 
			
		||||
  mask: _mask,
 | 
			
		||||
  toArrayBuffer,
 | 
			
		||||
  toBuffer,
 | 
			
		||||
  unmask: _unmask
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* istanbul ignore else  */
 | 
			
		||||
if (!process.env.WS_NO_BUFFER_UTIL) {
 | 
			
		||||
  try {
 | 
			
		||||
    const bufferUtil = require('bufferutil');
 | 
			
		||||
 | 
			
		||||
    module.exports.mask = function (source, mask, output, offset, length) {
 | 
			
		||||
      if (length < 48) _mask(source, mask, output, offset, length);
 | 
			
		||||
      else bufferUtil.mask(source, mask, output, offset, length);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    module.exports.unmask = function (buffer, mask) {
 | 
			
		||||
      if (buffer.length < 32) _unmask(buffer, mask);
 | 
			
		||||
      else bufferUtil.unmask(buffer, mask);
 | 
			
		||||
    };
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    // Continue regardless of the error.
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								node_modules/ws/lib/constants.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								node_modules/ws/lib/constants.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
  BINARY_TYPES: ['nodebuffer', 'arraybuffer', 'fragments'],
 | 
			
		||||
  EMPTY_BUFFER: Buffer.alloc(0),
 | 
			
		||||
  GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
 | 
			
		||||
  kForOnEventAttribute: Symbol('kIsForOnEventAttribute'),
 | 
			
		||||
  kListener: Symbol('kListener'),
 | 
			
		||||
  kStatusCode: Symbol('status-code'),
 | 
			
		||||
  kWebSocket: Symbol('websocket'),
 | 
			
		||||
  NOOP: () => {}
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										292
									
								
								node_modules/ws/lib/event-target.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										292
									
								
								node_modules/ws/lib/event-target.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,292 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
const { kForOnEventAttribute, kListener } = require('./constants');
 | 
			
		||||
 | 
			
		||||
const kCode = Symbol('kCode');
 | 
			
		||||
const kData = Symbol('kData');
 | 
			
		||||
const kError = Symbol('kError');
 | 
			
		||||
const kMessage = Symbol('kMessage');
 | 
			
		||||
const kReason = Symbol('kReason');
 | 
			
		||||
const kTarget = Symbol('kTarget');
 | 
			
		||||
const kType = Symbol('kType');
 | 
			
		||||
const kWasClean = Symbol('kWasClean');
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class representing an event.
 | 
			
		||||
 */
 | 
			
		||||
class Event {
 | 
			
		||||
  /**
 | 
			
		||||
   * Create a new `Event`.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {String} type The name of the event
 | 
			
		||||
   * @throws {TypeError} If the `type` argument is not specified
 | 
			
		||||
   */
 | 
			
		||||
  constructor(type) {
 | 
			
		||||
    this[kTarget] = null;
 | 
			
		||||
    this[kType] = type;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @type {*}
 | 
			
		||||
   */
 | 
			
		||||
  get target() {
 | 
			
		||||
    return this[kTarget];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @type {String}
 | 
			
		||||
   */
 | 
			
		||||
  get type() {
 | 
			
		||||
    return this[kType];
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Object.defineProperty(Event.prototype, 'target', { enumerable: true });
 | 
			
		||||
Object.defineProperty(Event.prototype, 'type', { enumerable: true });
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class representing a close event.
 | 
			
		||||
 *
 | 
			
		||||
 * @extends Event
 | 
			
		||||
 */
 | 
			
		||||
class CloseEvent extends Event {
 | 
			
		||||
  /**
 | 
			
		||||
   * Create a new `CloseEvent`.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {String} type The name of the event
 | 
			
		||||
   * @param {Object} [options] A dictionary object that allows for setting
 | 
			
		||||
   *     attributes via object members of the same name
 | 
			
		||||
   * @param {Number} [options.code=0] The status code explaining why the
 | 
			
		||||
   *     connection was closed
 | 
			
		||||
   * @param {String} [options.reason=''] A human-readable string explaining why
 | 
			
		||||
   *     the connection was closed
 | 
			
		||||
   * @param {Boolean} [options.wasClean=false] Indicates whether or not the
 | 
			
		||||
   *     connection was cleanly closed
 | 
			
		||||
   */
 | 
			
		||||
  constructor(type, options = {}) {
 | 
			
		||||
    super(type);
 | 
			
		||||
 | 
			
		||||
    this[kCode] = options.code === undefined ? 0 : options.code;
 | 
			
		||||
    this[kReason] = options.reason === undefined ? '' : options.reason;
 | 
			
		||||
    this[kWasClean] = options.wasClean === undefined ? false : options.wasClean;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @type {Number}
 | 
			
		||||
   */
 | 
			
		||||
  get code() {
 | 
			
		||||
    return this[kCode];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @type {String}
 | 
			
		||||
   */
 | 
			
		||||
  get reason() {
 | 
			
		||||
    return this[kReason];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @type {Boolean}
 | 
			
		||||
   */
 | 
			
		||||
  get wasClean() {
 | 
			
		||||
    return this[kWasClean];
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Object.defineProperty(CloseEvent.prototype, 'code', { enumerable: true });
 | 
			
		||||
Object.defineProperty(CloseEvent.prototype, 'reason', { enumerable: true });
 | 
			
		||||
Object.defineProperty(CloseEvent.prototype, 'wasClean', { enumerable: true });
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class representing an error event.
 | 
			
		||||
 *
 | 
			
		||||
 * @extends Event
 | 
			
		||||
 */
 | 
			
		||||
class ErrorEvent extends Event {
 | 
			
		||||
  /**
 | 
			
		||||
   * Create a new `ErrorEvent`.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {String} type The name of the event
 | 
			
		||||
   * @param {Object} [options] A dictionary object that allows for setting
 | 
			
		||||
   *     attributes via object members of the same name
 | 
			
		||||
   * @param {*} [options.error=null] The error that generated this event
 | 
			
		||||
   * @param {String} [options.message=''] The error message
 | 
			
		||||
   */
 | 
			
		||||
  constructor(type, options = {}) {
 | 
			
		||||
    super(type);
 | 
			
		||||
 | 
			
		||||
    this[kError] = options.error === undefined ? null : options.error;
 | 
			
		||||
    this[kMessage] = options.message === undefined ? '' : options.message;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @type {*}
 | 
			
		||||
   */
 | 
			
		||||
  get error() {
 | 
			
		||||
    return this[kError];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @type {String}
 | 
			
		||||
   */
 | 
			
		||||
  get message() {
 | 
			
		||||
    return this[kMessage];
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Object.defineProperty(ErrorEvent.prototype, 'error', { enumerable: true });
 | 
			
		||||
Object.defineProperty(ErrorEvent.prototype, 'message', { enumerable: true });
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class representing a message event.
 | 
			
		||||
 *
 | 
			
		||||
 * @extends Event
 | 
			
		||||
 */
 | 
			
		||||
class MessageEvent extends Event {
 | 
			
		||||
  /**
 | 
			
		||||
   * Create a new `MessageEvent`.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {String} type The name of the event
 | 
			
		||||
   * @param {Object} [options] A dictionary object that allows for setting
 | 
			
		||||
   *     attributes via object members of the same name
 | 
			
		||||
   * @param {*} [options.data=null] The message content
 | 
			
		||||
   */
 | 
			
		||||
  constructor(type, options = {}) {
 | 
			
		||||
    super(type);
 | 
			
		||||
 | 
			
		||||
    this[kData] = options.data === undefined ? null : options.data;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @type {*}
 | 
			
		||||
   */
 | 
			
		||||
  get data() {
 | 
			
		||||
    return this[kData];
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Object.defineProperty(MessageEvent.prototype, 'data', { enumerable: true });
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This provides methods for emulating the `EventTarget` interface. It's not
 | 
			
		||||
 * meant to be used directly.
 | 
			
		||||
 *
 | 
			
		||||
 * @mixin
 | 
			
		||||
 */
 | 
			
		||||
const EventTarget = {
 | 
			
		||||
  /**
 | 
			
		||||
   * Register an event listener.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {String} type A string representing the event type to listen for
 | 
			
		||||
   * @param {(Function|Object)} handler The listener to add
 | 
			
		||||
   * @param {Object} [options] An options object specifies characteristics about
 | 
			
		||||
   *     the event listener
 | 
			
		||||
   * @param {Boolean} [options.once=false] A `Boolean` indicating that the
 | 
			
		||||
   *     listener should be invoked at most once after being added. If `true`,
 | 
			
		||||
   *     the listener would be automatically removed when invoked.
 | 
			
		||||
   * @public
 | 
			
		||||
   */
 | 
			
		||||
  addEventListener(type, handler, options = {}) {
 | 
			
		||||
    for (const listener of this.listeners(type)) {
 | 
			
		||||
      if (
 | 
			
		||||
        !options[kForOnEventAttribute] &&
 | 
			
		||||
        listener[kListener] === handler &&
 | 
			
		||||
        !listener[kForOnEventAttribute]
 | 
			
		||||
      ) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let wrapper;
 | 
			
		||||
 | 
			
		||||
    if (type === 'message') {
 | 
			
		||||
      wrapper = function onMessage(data, isBinary) {
 | 
			
		||||
        const event = new MessageEvent('message', {
 | 
			
		||||
          data: isBinary ? data : data.toString()
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        event[kTarget] = this;
 | 
			
		||||
        callListener(handler, this, event);
 | 
			
		||||
      };
 | 
			
		||||
    } else if (type === 'close') {
 | 
			
		||||
      wrapper = function onClose(code, message) {
 | 
			
		||||
        const event = new CloseEvent('close', {
 | 
			
		||||
          code,
 | 
			
		||||
          reason: message.toString(),
 | 
			
		||||
          wasClean: this._closeFrameReceived && this._closeFrameSent
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        event[kTarget] = this;
 | 
			
		||||
        callListener(handler, this, event);
 | 
			
		||||
      };
 | 
			
		||||
    } else if (type === 'error') {
 | 
			
		||||
      wrapper = function onError(error) {
 | 
			
		||||
        const event = new ErrorEvent('error', {
 | 
			
		||||
          error,
 | 
			
		||||
          message: error.message
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        event[kTarget] = this;
 | 
			
		||||
        callListener(handler, this, event);
 | 
			
		||||
      };
 | 
			
		||||
    } else if (type === 'open') {
 | 
			
		||||
      wrapper = function onOpen() {
 | 
			
		||||
        const event = new Event('open');
 | 
			
		||||
 | 
			
		||||
        event[kTarget] = this;
 | 
			
		||||
        callListener(handler, this, event);
 | 
			
		||||
      };
 | 
			
		||||
    } else {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    wrapper[kForOnEventAttribute] = !!options[kForOnEventAttribute];
 | 
			
		||||
    wrapper[kListener] = handler;
 | 
			
		||||
 | 
			
		||||
    if (options.once) {
 | 
			
		||||
      this.once(type, wrapper);
 | 
			
		||||
    } else {
 | 
			
		||||
      this.on(type, wrapper);
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Remove an event listener.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {String} type A string representing the event type to remove
 | 
			
		||||
   * @param {(Function|Object)} handler The listener to remove
 | 
			
		||||
   * @public
 | 
			
		||||
   */
 | 
			
		||||
  removeEventListener(type, handler) {
 | 
			
		||||
    for (const listener of this.listeners(type)) {
 | 
			
		||||
      if (listener[kListener] === handler && !listener[kForOnEventAttribute]) {
 | 
			
		||||
        this.removeListener(type, listener);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
  CloseEvent,
 | 
			
		||||
  ErrorEvent,
 | 
			
		||||
  Event,
 | 
			
		||||
  EventTarget,
 | 
			
		||||
  MessageEvent
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Call an event listener
 | 
			
		||||
 *
 | 
			
		||||
 * @param {(Function|Object)} listener The listener to call
 | 
			
		||||
 * @param {*} thisArg The value to use as `this`` when calling the listener
 | 
			
		||||
 * @param {Event} event The event to pass to the listener
 | 
			
		||||
 * @private
 | 
			
		||||
 */
 | 
			
		||||
function callListener(listener, thisArg, event) {
 | 
			
		||||
  if (typeof listener === 'object' && listener.handleEvent) {
 | 
			
		||||
    listener.handleEvent.call(listener, event);
 | 
			
		||||
  } else {
 | 
			
		||||
    listener.call(thisArg, event);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										203
									
								
								node_modules/ws/lib/extension.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								node_modules/ws/lib/extension.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,203 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
const { tokenChars } = require('./validation');
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Adds an offer to the map of extension offers or a parameter to the map of
 | 
			
		||||
 * parameters.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {Object} dest The map of extension offers or parameters
 | 
			
		||||
 * @param {String} name The extension or parameter name
 | 
			
		||||
 * @param {(Object|Boolean|String)} elem The extension parameters or the
 | 
			
		||||
 *     parameter value
 | 
			
		||||
 * @private
 | 
			
		||||
 */
 | 
			
		||||
function push(dest, name, elem) {
 | 
			
		||||
  if (dest[name] === undefined) dest[name] = [elem];
 | 
			
		||||
  else dest[name].push(elem);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Parses the `Sec-WebSocket-Extensions` header into an object.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {String} header The field value of the header
 | 
			
		||||
 * @return {Object} The parsed object
 | 
			
		||||
 * @public
 | 
			
		||||
 */
 | 
			
		||||
function parse(header) {
 | 
			
		||||
  const offers = Object.create(null);
 | 
			
		||||
  let params = Object.create(null);
 | 
			
		||||
  let mustUnescape = false;
 | 
			
		||||
  let isEscaping = false;
 | 
			
		||||
  let inQuotes = false;
 | 
			
		||||
  let extensionName;
 | 
			
		||||
  let paramName;
 | 
			
		||||
  let start = -1;
 | 
			
		||||
  let code = -1;
 | 
			
		||||
  let end = -1;
 | 
			
		||||
  let i = 0;
 | 
			
		||||
 | 
			
		||||
  for (; i < header.length; i++) {
 | 
			
		||||
    code = header.charCodeAt(i);
 | 
			
		||||
 | 
			
		||||
    if (extensionName === undefined) {
 | 
			
		||||
      if (end === -1 && tokenChars[code] === 1) {
 | 
			
		||||
        if (start === -1) start = i;
 | 
			
		||||
      } else if (
 | 
			
		||||
        i !== 0 &&
 | 
			
		||||
        (code === 0x20 /* ' ' */ || code === 0x09) /* '\t' */
 | 
			
		||||
      ) {
 | 
			
		||||
        if (end === -1 && start !== -1) end = i;
 | 
			
		||||
      } else if (code === 0x3b /* ';' */ || code === 0x2c /* ',' */) {
 | 
			
		||||
        if (start === -1) {
 | 
			
		||||
          throw new SyntaxError(`Unexpected character at index ${i}`);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (end === -1) end = i;
 | 
			
		||||
        const name = header.slice(start, end);
 | 
			
		||||
        if (code === 0x2c) {
 | 
			
		||||
          push(offers, name, params);
 | 
			
		||||
          params = Object.create(null);
 | 
			
		||||
        } else {
 | 
			
		||||
          extensionName = name;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        start = end = -1;
 | 
			
		||||
      } else {
 | 
			
		||||
        throw new SyntaxError(`Unexpected character at index ${i}`);
 | 
			
		||||
      }
 | 
			
		||||
    } else if (paramName === undefined) {
 | 
			
		||||
      if (end === -1 && tokenChars[code] === 1) {
 | 
			
		||||
        if (start === -1) start = i;
 | 
			
		||||
      } else if (code === 0x20 || code === 0x09) {
 | 
			
		||||
        if (end === -1 && start !== -1) end = i;
 | 
			
		||||
      } else if (code === 0x3b || code === 0x2c) {
 | 
			
		||||
        if (start === -1) {
 | 
			
		||||
          throw new SyntaxError(`Unexpected character at index ${i}`);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (end === -1) end = i;
 | 
			
		||||
        push(params, header.slice(start, end), true);
 | 
			
		||||
        if (code === 0x2c) {
 | 
			
		||||
          push(offers, extensionName, params);
 | 
			
		||||
          params = Object.create(null);
 | 
			
		||||
          extensionName = undefined;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        start = end = -1;
 | 
			
		||||
      } else if (code === 0x3d /* '=' */ && start !== -1 && end === -1) {
 | 
			
		||||
        paramName = header.slice(start, i);
 | 
			
		||||
        start = end = -1;
 | 
			
		||||
      } else {
 | 
			
		||||
        throw new SyntaxError(`Unexpected character at index ${i}`);
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      //
 | 
			
		||||
      // The value of a quoted-string after unescaping must conform to the
 | 
			
		||||
      // token ABNF, so only token characters are valid.
 | 
			
		||||
      // Ref: https://tools.ietf.org/html/rfc6455#section-9.1
 | 
			
		||||
      //
 | 
			
		||||
      if (isEscaping) {
 | 
			
		||||
        if (tokenChars[code] !== 1) {
 | 
			
		||||
          throw new SyntaxError(`Unexpected character at index ${i}`);
 | 
			
		||||
        }
 | 
			
		||||
        if (start === -1) start = i;
 | 
			
		||||
        else if (!mustUnescape) mustUnescape = true;
 | 
			
		||||
        isEscaping = false;
 | 
			
		||||
      } else if (inQuotes) {
 | 
			
		||||
        if (tokenChars[code] === 1) {
 | 
			
		||||
          if (start === -1) start = i;
 | 
			
		||||
        } else if (code === 0x22 /* '"' */ && start !== -1) {
 | 
			
		||||
          inQuotes = false;
 | 
			
		||||
          end = i;
 | 
			
		||||
        } else if (code === 0x5c /* '\' */) {
 | 
			
		||||
          isEscaping = true;
 | 
			
		||||
        } else {
 | 
			
		||||
          throw new SyntaxError(`Unexpected character at index ${i}`);
 | 
			
		||||
        }
 | 
			
		||||
      } else if (code === 0x22 && header.charCodeAt(i - 1) === 0x3d) {
 | 
			
		||||
        inQuotes = true;
 | 
			
		||||
      } else if (end === -1 && tokenChars[code] === 1) {
 | 
			
		||||
        if (start === -1) start = i;
 | 
			
		||||
      } else if (start !== -1 && (code === 0x20 || code === 0x09)) {
 | 
			
		||||
        if (end === -1) end = i;
 | 
			
		||||
      } else if (code === 0x3b || code === 0x2c) {
 | 
			
		||||
        if (start === -1) {
 | 
			
		||||
          throw new SyntaxError(`Unexpected character at index ${i}`);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (end === -1) end = i;
 | 
			
		||||
        let value = header.slice(start, end);
 | 
			
		||||
        if (mustUnescape) {
 | 
			
		||||
          value = value.replace(/\\/g, '');
 | 
			
		||||
          mustUnescape = false;
 | 
			
		||||
        }
 | 
			
		||||
        push(params, paramName, value);
 | 
			
		||||
        if (code === 0x2c) {
 | 
			
		||||
          push(offers, extensionName, params);
 | 
			
		||||
          params = Object.create(null);
 | 
			
		||||
          extensionName = undefined;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        paramName = undefined;
 | 
			
		||||
        start = end = -1;
 | 
			
		||||
      } else {
 | 
			
		||||
        throw new SyntaxError(`Unexpected character at index ${i}`);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (start === -1 || inQuotes || code === 0x20 || code === 0x09) {
 | 
			
		||||
    throw new SyntaxError('Unexpected end of input');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (end === -1) end = i;
 | 
			
		||||
  const token = header.slice(start, end);
 | 
			
		||||
  if (extensionName === undefined) {
 | 
			
		||||
    push(offers, token, params);
 | 
			
		||||
  } else {
 | 
			
		||||
    if (paramName === undefined) {
 | 
			
		||||
      push(params, token, true);
 | 
			
		||||
    } else if (mustUnescape) {
 | 
			
		||||
      push(params, paramName, token.replace(/\\/g, ''));
 | 
			
		||||
    } else {
 | 
			
		||||
      push(params, paramName, token);
 | 
			
		||||
    }
 | 
			
		||||
    push(offers, extensionName, params);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return offers;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Builds the `Sec-WebSocket-Extensions` header field value.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {Object} extensions The map of extensions and parameters to format
 | 
			
		||||
 * @return {String} A string representing the given object
 | 
			
		||||
 * @public
 | 
			
		||||
 */
 | 
			
		||||
function format(extensions) {
 | 
			
		||||
  return Object.keys(extensions)
 | 
			
		||||
    .map((extension) => {
 | 
			
		||||
      let configurations = extensions[extension];
 | 
			
		||||
      if (!Array.isArray(configurations)) configurations = [configurations];
 | 
			
		||||
      return configurations
 | 
			
		||||
        .map((params) => {
 | 
			
		||||
          return [extension]
 | 
			
		||||
            .concat(
 | 
			
		||||
              Object.keys(params).map((k) => {
 | 
			
		||||
                let values = params[k];
 | 
			
		||||
                if (!Array.isArray(values)) values = [values];
 | 
			
		||||
                return values
 | 
			
		||||
                  .map((v) => (v === true ? k : `${k}=${v}`))
 | 
			
		||||
                  .join('; ');
 | 
			
		||||
              })
 | 
			
		||||
            )
 | 
			
		||||
            .join('; ');
 | 
			
		||||
        })
 | 
			
		||||
        .join(', ');
 | 
			
		||||
    })
 | 
			
		||||
    .join(', ');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = { format, parse };
 | 
			
		||||
							
								
								
									
										55
									
								
								node_modules/ws/lib/limiter.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								node_modules/ws/lib/limiter.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
const kDone = Symbol('kDone');
 | 
			
		||||
const kRun = Symbol('kRun');
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A very simple job queue with adjustable concurrency. Adapted from
 | 
			
		||||
 * https://github.com/STRML/async-limiter
 | 
			
		||||
 */
 | 
			
		||||
class Limiter {
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates a new `Limiter`.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Number} [concurrency=Infinity] The maximum number of jobs allowed
 | 
			
		||||
   *     to run concurrently
 | 
			
		||||
   */
 | 
			
		||||
  constructor(concurrency) {
 | 
			
		||||
    this[kDone] = () => {
 | 
			
		||||
      this.pending--;
 | 
			
		||||
      this[kRun]();
 | 
			
		||||
    };
 | 
			
		||||
    this.concurrency = concurrency || Infinity;
 | 
			
		||||
    this.jobs = [];
 | 
			
		||||
    this.pending = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Adds a job to the queue.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Function} job The job to run
 | 
			
		||||
   * @public
 | 
			
		||||
   */
 | 
			
		||||
  add(job) {
 | 
			
		||||
    this.jobs.push(job);
 | 
			
		||||
    this[kRun]();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Removes a job from the queue and runs it if possible.
 | 
			
		||||
   *
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  [kRun]() {
 | 
			
		||||
    if (this.pending === this.concurrency) return;
 | 
			
		||||
 | 
			
		||||
    if (this.jobs.length) {
 | 
			
		||||
      const job = this.jobs.shift();
 | 
			
		||||
 | 
			
		||||
      this.pending++;
 | 
			
		||||
      job(this[kDone]);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = Limiter;
 | 
			
		||||
							
								
								
									
										514
									
								
								node_modules/ws/lib/permessage-deflate.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										514
									
								
								node_modules/ws/lib/permessage-deflate.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,514 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
const zlib = require('zlib');
 | 
			
		||||
 | 
			
		||||
const bufferUtil = require('./buffer-util');
 | 
			
		||||
const Limiter = require('./limiter');
 | 
			
		||||
const { kStatusCode } = require('./constants');
 | 
			
		||||
 | 
			
		||||
const FastBuffer = Buffer[Symbol.species];
 | 
			
		||||
const TRAILER = Buffer.from([0x00, 0x00, 0xff, 0xff]);
 | 
			
		||||
const kPerMessageDeflate = Symbol('permessage-deflate');
 | 
			
		||||
const kTotalLength = Symbol('total-length');
 | 
			
		||||
const kCallback = Symbol('callback');
 | 
			
		||||
const kBuffers = Symbol('buffers');
 | 
			
		||||
const kError = Symbol('error');
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// We limit zlib concurrency, which prevents severe memory fragmentation
 | 
			
		||||
// as documented in https://github.com/nodejs/node/issues/8871#issuecomment-250915913
 | 
			
		||||
// and https://github.com/websockets/ws/issues/1202
 | 
			
		||||
//
 | 
			
		||||
// Intentionally global; it's the global thread pool that's an issue.
 | 
			
		||||
//
 | 
			
		||||
let zlibLimiter;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * permessage-deflate implementation.
 | 
			
		||||
 */
 | 
			
		||||
class PerMessageDeflate {
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates a PerMessageDeflate instance.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Object} [options] Configuration options
 | 
			
		||||
   * @param {(Boolean|Number)} [options.clientMaxWindowBits] Advertise support
 | 
			
		||||
   *     for, or request, a custom client window size
 | 
			
		||||
   * @param {Boolean} [options.clientNoContextTakeover=false] Advertise/
 | 
			
		||||
   *     acknowledge disabling of client context takeover
 | 
			
		||||
   * @param {Number} [options.concurrencyLimit=10] The number of concurrent
 | 
			
		||||
   *     calls to zlib
 | 
			
		||||
   * @param {(Boolean|Number)} [options.serverMaxWindowBits] Request/confirm the
 | 
			
		||||
   *     use of a custom server window size
 | 
			
		||||
   * @param {Boolean} [options.serverNoContextTakeover=false] Request/accept
 | 
			
		||||
   *     disabling of server context takeover
 | 
			
		||||
   * @param {Number} [options.threshold=1024] Size (in bytes) below which
 | 
			
		||||
   *     messages should not be compressed if context takeover is disabled
 | 
			
		||||
   * @param {Object} [options.zlibDeflateOptions] Options to pass to zlib on
 | 
			
		||||
   *     deflate
 | 
			
		||||
   * @param {Object} [options.zlibInflateOptions] Options to pass to zlib on
 | 
			
		||||
   *     inflate
 | 
			
		||||
   * @param {Boolean} [isServer=false] Create the instance in either server or
 | 
			
		||||
   *     client mode
 | 
			
		||||
   * @param {Number} [maxPayload=0] The maximum allowed message length
 | 
			
		||||
   */
 | 
			
		||||
  constructor(options, isServer, maxPayload) {
 | 
			
		||||
    this._maxPayload = maxPayload | 0;
 | 
			
		||||
    this._options = options || {};
 | 
			
		||||
    this._threshold =
 | 
			
		||||
      this._options.threshold !== undefined ? this._options.threshold : 1024;
 | 
			
		||||
    this._isServer = !!isServer;
 | 
			
		||||
    this._deflate = null;
 | 
			
		||||
    this._inflate = null;
 | 
			
		||||
 | 
			
		||||
    this.params = null;
 | 
			
		||||
 | 
			
		||||
    if (!zlibLimiter) {
 | 
			
		||||
      const concurrency =
 | 
			
		||||
        this._options.concurrencyLimit !== undefined
 | 
			
		||||
          ? this._options.concurrencyLimit
 | 
			
		||||
          : 10;
 | 
			
		||||
      zlibLimiter = new Limiter(concurrency);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @type {String}
 | 
			
		||||
   */
 | 
			
		||||
  static get extensionName() {
 | 
			
		||||
    return 'permessage-deflate';
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Create an extension negotiation offer.
 | 
			
		||||
   *
 | 
			
		||||
   * @return {Object} Extension parameters
 | 
			
		||||
   * @public
 | 
			
		||||
   */
 | 
			
		||||
  offer() {
 | 
			
		||||
    const params = {};
 | 
			
		||||
 | 
			
		||||
    if (this._options.serverNoContextTakeover) {
 | 
			
		||||
      params.server_no_context_takeover = true;
 | 
			
		||||
    }
 | 
			
		||||
    if (this._options.clientNoContextTakeover) {
 | 
			
		||||
      params.client_no_context_takeover = true;
 | 
			
		||||
    }
 | 
			
		||||
    if (this._options.serverMaxWindowBits) {
 | 
			
		||||
      params.server_max_window_bits = this._options.serverMaxWindowBits;
 | 
			
		||||
    }
 | 
			
		||||
    if (this._options.clientMaxWindowBits) {
 | 
			
		||||
      params.client_max_window_bits = this._options.clientMaxWindowBits;
 | 
			
		||||
    } else if (this._options.clientMaxWindowBits == null) {
 | 
			
		||||
      params.client_max_window_bits = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return params;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Accept an extension negotiation offer/response.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Array} configurations The extension negotiation offers/reponse
 | 
			
		||||
   * @return {Object} Accepted configuration
 | 
			
		||||
   * @public
 | 
			
		||||
   */
 | 
			
		||||
  accept(configurations) {
 | 
			
		||||
    configurations = this.normalizeParams(configurations);
 | 
			
		||||
 | 
			
		||||
    this.params = this._isServer
 | 
			
		||||
      ? this.acceptAsServer(configurations)
 | 
			
		||||
      : this.acceptAsClient(configurations);
 | 
			
		||||
 | 
			
		||||
    return this.params;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Releases all resources used by the extension.
 | 
			
		||||
   *
 | 
			
		||||
   * @public
 | 
			
		||||
   */
 | 
			
		||||
  cleanup() {
 | 
			
		||||
    if (this._inflate) {
 | 
			
		||||
      this._inflate.close();
 | 
			
		||||
      this._inflate = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this._deflate) {
 | 
			
		||||
      const callback = this._deflate[kCallback];
 | 
			
		||||
 | 
			
		||||
      this._deflate.close();
 | 
			
		||||
      this._deflate = null;
 | 
			
		||||
 | 
			
		||||
      if (callback) {
 | 
			
		||||
        callback(
 | 
			
		||||
          new Error(
 | 
			
		||||
            'The deflate stream was closed while data was being processed'
 | 
			
		||||
          )
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   *  Accept an extension negotiation offer.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Array} offers The extension negotiation offers
 | 
			
		||||
   * @return {Object} Accepted configuration
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  acceptAsServer(offers) {
 | 
			
		||||
    const opts = this._options;
 | 
			
		||||
    const accepted = offers.find((params) => {
 | 
			
		||||
      if (
 | 
			
		||||
        (opts.serverNoContextTakeover === false &&
 | 
			
		||||
          params.server_no_context_takeover) ||
 | 
			
		||||
        (params.server_max_window_bits &&
 | 
			
		||||
          (opts.serverMaxWindowBits === false ||
 | 
			
		||||
            (typeof opts.serverMaxWindowBits === 'number' &&
 | 
			
		||||
              opts.serverMaxWindowBits > params.server_max_window_bits))) ||
 | 
			
		||||
        (typeof opts.clientMaxWindowBits === 'number' &&
 | 
			
		||||
          !params.client_max_window_bits)
 | 
			
		||||
      ) {
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return true;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (!accepted) {
 | 
			
		||||
      throw new Error('None of the extension offers can be accepted');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (opts.serverNoContextTakeover) {
 | 
			
		||||
      accepted.server_no_context_takeover = true;
 | 
			
		||||
    }
 | 
			
		||||
    if (opts.clientNoContextTakeover) {
 | 
			
		||||
      accepted.client_no_context_takeover = true;
 | 
			
		||||
    }
 | 
			
		||||
    if (typeof opts.serverMaxWindowBits === 'number') {
 | 
			
		||||
      accepted.server_max_window_bits = opts.serverMaxWindowBits;
 | 
			
		||||
    }
 | 
			
		||||
    if (typeof opts.clientMaxWindowBits === 'number') {
 | 
			
		||||
      accepted.client_max_window_bits = opts.clientMaxWindowBits;
 | 
			
		||||
    } else if (
 | 
			
		||||
      accepted.client_max_window_bits === true ||
 | 
			
		||||
      opts.clientMaxWindowBits === false
 | 
			
		||||
    ) {
 | 
			
		||||
      delete accepted.client_max_window_bits;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return accepted;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Accept the extension negotiation response.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Array} response The extension negotiation response
 | 
			
		||||
   * @return {Object} Accepted configuration
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  acceptAsClient(response) {
 | 
			
		||||
    const params = response[0];
 | 
			
		||||
 | 
			
		||||
    if (
 | 
			
		||||
      this._options.clientNoContextTakeover === false &&
 | 
			
		||||
      params.client_no_context_takeover
 | 
			
		||||
    ) {
 | 
			
		||||
      throw new Error('Unexpected parameter "client_no_context_takeover"');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!params.client_max_window_bits) {
 | 
			
		||||
      if (typeof this._options.clientMaxWindowBits === 'number') {
 | 
			
		||||
        params.client_max_window_bits = this._options.clientMaxWindowBits;
 | 
			
		||||
      }
 | 
			
		||||
    } else if (
 | 
			
		||||
      this._options.clientMaxWindowBits === false ||
 | 
			
		||||
      (typeof this._options.clientMaxWindowBits === 'number' &&
 | 
			
		||||
        params.client_max_window_bits > this._options.clientMaxWindowBits)
 | 
			
		||||
    ) {
 | 
			
		||||
      throw new Error(
 | 
			
		||||
        'Unexpected or invalid parameter "client_max_window_bits"'
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return params;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Normalize parameters.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Array} configurations The extension negotiation offers/reponse
 | 
			
		||||
   * @return {Array} The offers/response with normalized parameters
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  normalizeParams(configurations) {
 | 
			
		||||
    configurations.forEach((params) => {
 | 
			
		||||
      Object.keys(params).forEach((key) => {
 | 
			
		||||
        let value = params[key];
 | 
			
		||||
 | 
			
		||||
        if (value.length > 1) {
 | 
			
		||||
          throw new Error(`Parameter "${key}" must have only a single value`);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        value = value[0];
 | 
			
		||||
 | 
			
		||||
        if (key === 'client_max_window_bits') {
 | 
			
		||||
          if (value !== true) {
 | 
			
		||||
            const num = +value;
 | 
			
		||||
            if (!Number.isInteger(num) || num < 8 || num > 15) {
 | 
			
		||||
              throw new TypeError(
 | 
			
		||||
                `Invalid value for parameter "${key}": ${value}`
 | 
			
		||||
              );
 | 
			
		||||
            }
 | 
			
		||||
            value = num;
 | 
			
		||||
          } else if (!this._isServer) {
 | 
			
		||||
            throw new TypeError(
 | 
			
		||||
              `Invalid value for parameter "${key}": ${value}`
 | 
			
		||||
            );
 | 
			
		||||
          }
 | 
			
		||||
        } else if (key === 'server_max_window_bits') {
 | 
			
		||||
          const num = +value;
 | 
			
		||||
          if (!Number.isInteger(num) || num < 8 || num > 15) {
 | 
			
		||||
            throw new TypeError(
 | 
			
		||||
              `Invalid value for parameter "${key}": ${value}`
 | 
			
		||||
            );
 | 
			
		||||
          }
 | 
			
		||||
          value = num;
 | 
			
		||||
        } else if (
 | 
			
		||||
          key === 'client_no_context_takeover' ||
 | 
			
		||||
          key === 'server_no_context_takeover'
 | 
			
		||||
        ) {
 | 
			
		||||
          if (value !== true) {
 | 
			
		||||
            throw new TypeError(
 | 
			
		||||
              `Invalid value for parameter "${key}": ${value}`
 | 
			
		||||
            );
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          throw new Error(`Unknown parameter "${key}"`);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        params[key] = value;
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return configurations;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Decompress data. Concurrency limited.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Buffer} data Compressed data
 | 
			
		||||
   * @param {Boolean} fin Specifies whether or not this is the last fragment
 | 
			
		||||
   * @param {Function} callback Callback
 | 
			
		||||
   * @public
 | 
			
		||||
   */
 | 
			
		||||
  decompress(data, fin, callback) {
 | 
			
		||||
    zlibLimiter.add((done) => {
 | 
			
		||||
      this._decompress(data, fin, (err, result) => {
 | 
			
		||||
        done();
 | 
			
		||||
        callback(err, result);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Compress data. Concurrency limited.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {(Buffer|String)} data Data to compress
 | 
			
		||||
   * @param {Boolean} fin Specifies whether or not this is the last fragment
 | 
			
		||||
   * @param {Function} callback Callback
 | 
			
		||||
   * @public
 | 
			
		||||
   */
 | 
			
		||||
  compress(data, fin, callback) {
 | 
			
		||||
    zlibLimiter.add((done) => {
 | 
			
		||||
      this._compress(data, fin, (err, result) => {
 | 
			
		||||
        done();
 | 
			
		||||
        callback(err, result);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Decompress data.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Buffer} data Compressed data
 | 
			
		||||
   * @param {Boolean} fin Specifies whether or not this is the last fragment
 | 
			
		||||
   * @param {Function} callback Callback
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  _decompress(data, fin, callback) {
 | 
			
		||||
    const endpoint = this._isServer ? 'client' : 'server';
 | 
			
		||||
 | 
			
		||||
    if (!this._inflate) {
 | 
			
		||||
      const key = `${endpoint}_max_window_bits`;
 | 
			
		||||
      const windowBits =
 | 
			
		||||
        typeof this.params[key] !== 'number'
 | 
			
		||||
          ? zlib.Z_DEFAULT_WINDOWBITS
 | 
			
		||||
          : this.params[key];
 | 
			
		||||
 | 
			
		||||
      this._inflate = zlib.createInflateRaw({
 | 
			
		||||
        ...this._options.zlibInflateOptions,
 | 
			
		||||
        windowBits
 | 
			
		||||
      });
 | 
			
		||||
      this._inflate[kPerMessageDeflate] = this;
 | 
			
		||||
      this._inflate[kTotalLength] = 0;
 | 
			
		||||
      this._inflate[kBuffers] = [];
 | 
			
		||||
      this._inflate.on('error', inflateOnError);
 | 
			
		||||
      this._inflate.on('data', inflateOnData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this._inflate[kCallback] = callback;
 | 
			
		||||
 | 
			
		||||
    this._inflate.write(data);
 | 
			
		||||
    if (fin) this._inflate.write(TRAILER);
 | 
			
		||||
 | 
			
		||||
    this._inflate.flush(() => {
 | 
			
		||||
      const err = this._inflate[kError];
 | 
			
		||||
 | 
			
		||||
      if (err) {
 | 
			
		||||
        this._inflate.close();
 | 
			
		||||
        this._inflate = null;
 | 
			
		||||
        callback(err);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const data = bufferUtil.concat(
 | 
			
		||||
        this._inflate[kBuffers],
 | 
			
		||||
        this._inflate[kTotalLength]
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      if (this._inflate._readableState.endEmitted) {
 | 
			
		||||
        this._inflate.close();
 | 
			
		||||
        this._inflate = null;
 | 
			
		||||
      } else {
 | 
			
		||||
        this._inflate[kTotalLength] = 0;
 | 
			
		||||
        this._inflate[kBuffers] = [];
 | 
			
		||||
 | 
			
		||||
        if (fin && this.params[`${endpoint}_no_context_takeover`]) {
 | 
			
		||||
          this._inflate.reset();
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      callback(null, data);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Compress data.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {(Buffer|String)} data Data to compress
 | 
			
		||||
   * @param {Boolean} fin Specifies whether or not this is the last fragment
 | 
			
		||||
   * @param {Function} callback Callback
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  _compress(data, fin, callback) {
 | 
			
		||||
    const endpoint = this._isServer ? 'server' : 'client';
 | 
			
		||||
 | 
			
		||||
    if (!this._deflate) {
 | 
			
		||||
      const key = `${endpoint}_max_window_bits`;
 | 
			
		||||
      const windowBits =
 | 
			
		||||
        typeof this.params[key] !== 'number'
 | 
			
		||||
          ? zlib.Z_DEFAULT_WINDOWBITS
 | 
			
		||||
          : this.params[key];
 | 
			
		||||
 | 
			
		||||
      this._deflate = zlib.createDeflateRaw({
 | 
			
		||||
        ...this._options.zlibDeflateOptions,
 | 
			
		||||
        windowBits
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      this._deflate[kTotalLength] = 0;
 | 
			
		||||
      this._deflate[kBuffers] = [];
 | 
			
		||||
 | 
			
		||||
      this._deflate.on('data', deflateOnData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this._deflate[kCallback] = callback;
 | 
			
		||||
 | 
			
		||||
    this._deflate.write(data);
 | 
			
		||||
    this._deflate.flush(zlib.Z_SYNC_FLUSH, () => {
 | 
			
		||||
      if (!this._deflate) {
 | 
			
		||||
        //
 | 
			
		||||
        // The deflate stream was closed while data was being processed.
 | 
			
		||||
        //
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      let data = bufferUtil.concat(
 | 
			
		||||
        this._deflate[kBuffers],
 | 
			
		||||
        this._deflate[kTotalLength]
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      if (fin) {
 | 
			
		||||
        data = new FastBuffer(data.buffer, data.byteOffset, data.length - 4);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      //
 | 
			
		||||
      // Ensure that the callback will not be called again in
 | 
			
		||||
      // `PerMessageDeflate#cleanup()`.
 | 
			
		||||
      //
 | 
			
		||||
      this._deflate[kCallback] = null;
 | 
			
		||||
 | 
			
		||||
      this._deflate[kTotalLength] = 0;
 | 
			
		||||
      this._deflate[kBuffers] = [];
 | 
			
		||||
 | 
			
		||||
      if (fin && this.params[`${endpoint}_no_context_takeover`]) {
 | 
			
		||||
        this._deflate.reset();
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      callback(null, data);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = PerMessageDeflate;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The listener of the `zlib.DeflateRaw` stream `'data'` event.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {Buffer} chunk A chunk of data
 | 
			
		||||
 * @private
 | 
			
		||||
 */
 | 
			
		||||
function deflateOnData(chunk) {
 | 
			
		||||
  this[kBuffers].push(chunk);
 | 
			
		||||
  this[kTotalLength] += chunk.length;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The listener of the `zlib.InflateRaw` stream `'data'` event.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {Buffer} chunk A chunk of data
 | 
			
		||||
 * @private
 | 
			
		||||
 */
 | 
			
		||||
function inflateOnData(chunk) {
 | 
			
		||||
  this[kTotalLength] += chunk.length;
 | 
			
		||||
 | 
			
		||||
  if (
 | 
			
		||||
    this[kPerMessageDeflate]._maxPayload < 1 ||
 | 
			
		||||
    this[kTotalLength] <= this[kPerMessageDeflate]._maxPayload
 | 
			
		||||
  ) {
 | 
			
		||||
    this[kBuffers].push(chunk);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this[kError] = new RangeError('Max payload size exceeded');
 | 
			
		||||
  this[kError].code = 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH';
 | 
			
		||||
  this[kError][kStatusCode] = 1009;
 | 
			
		||||
  this.removeListener('data', inflateOnData);
 | 
			
		||||
  this.reset();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The listener of the `zlib.InflateRaw` stream `'error'` event.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {Error} err The emitted error
 | 
			
		||||
 * @private
 | 
			
		||||
 */
 | 
			
		||||
function inflateOnError(err) {
 | 
			
		||||
  //
 | 
			
		||||
  // There is no need to call `Zlib#close()` as the handle is automatically
 | 
			
		||||
  // closed when an error is emitted.
 | 
			
		||||
  //
 | 
			
		||||
  this[kPerMessageDeflate]._inflate = null;
 | 
			
		||||
  err[kStatusCode] = 1007;
 | 
			
		||||
  this[kCallback](err);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										627
									
								
								node_modules/ws/lib/receiver.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										627
									
								
								node_modules/ws/lib/receiver.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,627 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
const { Writable } = require('stream');
 | 
			
		||||
 | 
			
		||||
const PerMessageDeflate = require('./permessage-deflate');
 | 
			
		||||
const {
 | 
			
		||||
  BINARY_TYPES,
 | 
			
		||||
  EMPTY_BUFFER,
 | 
			
		||||
  kStatusCode,
 | 
			
		||||
  kWebSocket
 | 
			
		||||
} = require('./constants');
 | 
			
		||||
const { concat, toArrayBuffer, unmask } = require('./buffer-util');
 | 
			
		||||
const { isValidStatusCode, isValidUTF8 } = require('./validation');
 | 
			
		||||
 | 
			
		||||
const FastBuffer = Buffer[Symbol.species];
 | 
			
		||||
const GET_INFO = 0;
 | 
			
		||||
const GET_PAYLOAD_LENGTH_16 = 1;
 | 
			
		||||
const GET_PAYLOAD_LENGTH_64 = 2;
 | 
			
		||||
const GET_MASK = 3;
 | 
			
		||||
const GET_DATA = 4;
 | 
			
		||||
const INFLATING = 5;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * HyBi Receiver implementation.
 | 
			
		||||
 *
 | 
			
		||||
 * @extends Writable
 | 
			
		||||
 */
 | 
			
		||||
class Receiver extends Writable {
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates a Receiver instance.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Object} [options] Options object
 | 
			
		||||
   * @param {String} [options.binaryType=nodebuffer] The type for binary data
 | 
			
		||||
   * @param {Object} [options.extensions] An object containing the negotiated
 | 
			
		||||
   *     extensions
 | 
			
		||||
   * @param {Boolean} [options.isServer=false] Specifies whether to operate in
 | 
			
		||||
   *     client or server mode
 | 
			
		||||
   * @param {Number} [options.maxPayload=0] The maximum allowed message length
 | 
			
		||||
   * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
 | 
			
		||||
   *     not to skip UTF-8 validation for text and close messages
 | 
			
		||||
   */
 | 
			
		||||
  constructor(options = {}) {
 | 
			
		||||
    super();
 | 
			
		||||
 | 
			
		||||
    this._binaryType = options.binaryType || BINARY_TYPES[0];
 | 
			
		||||
    this._extensions = options.extensions || {};
 | 
			
		||||
    this._isServer = !!options.isServer;
 | 
			
		||||
    this._maxPayload = options.maxPayload | 0;
 | 
			
		||||
    this._skipUTF8Validation = !!options.skipUTF8Validation;
 | 
			
		||||
    this[kWebSocket] = undefined;
 | 
			
		||||
 | 
			
		||||
    this._bufferedBytes = 0;
 | 
			
		||||
    this._buffers = [];
 | 
			
		||||
 | 
			
		||||
    this._compressed = false;
 | 
			
		||||
    this._payloadLength = 0;
 | 
			
		||||
    this._mask = undefined;
 | 
			
		||||
    this._fragmented = 0;
 | 
			
		||||
    this._masked = false;
 | 
			
		||||
    this._fin = false;
 | 
			
		||||
    this._opcode = 0;
 | 
			
		||||
 | 
			
		||||
    this._totalPayloadLength = 0;
 | 
			
		||||
    this._messageLength = 0;
 | 
			
		||||
    this._fragments = [];
 | 
			
		||||
 | 
			
		||||
    this._state = GET_INFO;
 | 
			
		||||
    this._loop = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Implements `Writable.prototype._write()`.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Buffer} chunk The chunk of data to write
 | 
			
		||||
   * @param {String} encoding The character encoding of `chunk`
 | 
			
		||||
   * @param {Function} cb Callback
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  _write(chunk, encoding, cb) {
 | 
			
		||||
    if (this._opcode === 0x08 && this._state == GET_INFO) return cb();
 | 
			
		||||
 | 
			
		||||
    this._bufferedBytes += chunk.length;
 | 
			
		||||
    this._buffers.push(chunk);
 | 
			
		||||
    this.startLoop(cb);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Consumes `n` bytes from the buffered data.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Number} n The number of bytes to consume
 | 
			
		||||
   * @return {Buffer} The consumed bytes
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  consume(n) {
 | 
			
		||||
    this._bufferedBytes -= n;
 | 
			
		||||
 | 
			
		||||
    if (n === this._buffers[0].length) return this._buffers.shift();
 | 
			
		||||
 | 
			
		||||
    if (n < this._buffers[0].length) {
 | 
			
		||||
      const buf = this._buffers[0];
 | 
			
		||||
      this._buffers[0] = new FastBuffer(
 | 
			
		||||
        buf.buffer,
 | 
			
		||||
        buf.byteOffset + n,
 | 
			
		||||
        buf.length - n
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      return new FastBuffer(buf.buffer, buf.byteOffset, n);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const dst = Buffer.allocUnsafe(n);
 | 
			
		||||
 | 
			
		||||
    do {
 | 
			
		||||
      const buf = this._buffers[0];
 | 
			
		||||
      const offset = dst.length - n;
 | 
			
		||||
 | 
			
		||||
      if (n >= buf.length) {
 | 
			
		||||
        dst.set(this._buffers.shift(), offset);
 | 
			
		||||
      } else {
 | 
			
		||||
        dst.set(new Uint8Array(buf.buffer, buf.byteOffset, n), offset);
 | 
			
		||||
        this._buffers[0] = new FastBuffer(
 | 
			
		||||
          buf.buffer,
 | 
			
		||||
          buf.byteOffset + n,
 | 
			
		||||
          buf.length - n
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      n -= buf.length;
 | 
			
		||||
    } while (n > 0);
 | 
			
		||||
 | 
			
		||||
    return dst;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Starts the parsing loop.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Function} cb Callback
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  startLoop(cb) {
 | 
			
		||||
    let err;
 | 
			
		||||
    this._loop = true;
 | 
			
		||||
 | 
			
		||||
    do {
 | 
			
		||||
      switch (this._state) {
 | 
			
		||||
        case GET_INFO:
 | 
			
		||||
          err = this.getInfo();
 | 
			
		||||
          break;
 | 
			
		||||
        case GET_PAYLOAD_LENGTH_16:
 | 
			
		||||
          err = this.getPayloadLength16();
 | 
			
		||||
          break;
 | 
			
		||||
        case GET_PAYLOAD_LENGTH_64:
 | 
			
		||||
          err = this.getPayloadLength64();
 | 
			
		||||
          break;
 | 
			
		||||
        case GET_MASK:
 | 
			
		||||
          this.getMask();
 | 
			
		||||
          break;
 | 
			
		||||
        case GET_DATA:
 | 
			
		||||
          err = this.getData(cb);
 | 
			
		||||
          break;
 | 
			
		||||
        default:
 | 
			
		||||
          // `INFLATING`
 | 
			
		||||
          this._loop = false;
 | 
			
		||||
          return;
 | 
			
		||||
      }
 | 
			
		||||
    } while (this._loop);
 | 
			
		||||
 | 
			
		||||
    cb(err);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Reads the first two bytes of a frame.
 | 
			
		||||
   *
 | 
			
		||||
   * @return {(RangeError|undefined)} A possible error
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  getInfo() {
 | 
			
		||||
    if (this._bufferedBytes < 2) {
 | 
			
		||||
      this._loop = false;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const buf = this.consume(2);
 | 
			
		||||
 | 
			
		||||
    if ((buf[0] & 0x30) !== 0x00) {
 | 
			
		||||
      this._loop = false;
 | 
			
		||||
      return error(
 | 
			
		||||
        RangeError,
 | 
			
		||||
        'RSV2 and RSV3 must be clear',
 | 
			
		||||
        true,
 | 
			
		||||
        1002,
 | 
			
		||||
        'WS_ERR_UNEXPECTED_RSV_2_3'
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const compressed = (buf[0] & 0x40) === 0x40;
 | 
			
		||||
 | 
			
		||||
    if (compressed && !this._extensions[PerMessageDeflate.extensionName]) {
 | 
			
		||||
      this._loop = false;
 | 
			
		||||
      return error(
 | 
			
		||||
        RangeError,
 | 
			
		||||
        'RSV1 must be clear',
 | 
			
		||||
        true,
 | 
			
		||||
        1002,
 | 
			
		||||
        'WS_ERR_UNEXPECTED_RSV_1'
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this._fin = (buf[0] & 0x80) === 0x80;
 | 
			
		||||
    this._opcode = buf[0] & 0x0f;
 | 
			
		||||
    this._payloadLength = buf[1] & 0x7f;
 | 
			
		||||
 | 
			
		||||
    if (this._opcode === 0x00) {
 | 
			
		||||
      if (compressed) {
 | 
			
		||||
        this._loop = false;
 | 
			
		||||
        return error(
 | 
			
		||||
          RangeError,
 | 
			
		||||
          'RSV1 must be clear',
 | 
			
		||||
          true,
 | 
			
		||||
          1002,
 | 
			
		||||
          'WS_ERR_UNEXPECTED_RSV_1'
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!this._fragmented) {
 | 
			
		||||
        this._loop = false;
 | 
			
		||||
        return error(
 | 
			
		||||
          RangeError,
 | 
			
		||||
          'invalid opcode 0',
 | 
			
		||||
          true,
 | 
			
		||||
          1002,
 | 
			
		||||
          'WS_ERR_INVALID_OPCODE'
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      this._opcode = this._fragmented;
 | 
			
		||||
    } else if (this._opcode === 0x01 || this._opcode === 0x02) {
 | 
			
		||||
      if (this._fragmented) {
 | 
			
		||||
        this._loop = false;
 | 
			
		||||
        return error(
 | 
			
		||||
          RangeError,
 | 
			
		||||
          `invalid opcode ${this._opcode}`,
 | 
			
		||||
          true,
 | 
			
		||||
          1002,
 | 
			
		||||
          'WS_ERR_INVALID_OPCODE'
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      this._compressed = compressed;
 | 
			
		||||
    } else if (this._opcode > 0x07 && this._opcode < 0x0b) {
 | 
			
		||||
      if (!this._fin) {
 | 
			
		||||
        this._loop = false;
 | 
			
		||||
        return error(
 | 
			
		||||
          RangeError,
 | 
			
		||||
          'FIN must be set',
 | 
			
		||||
          true,
 | 
			
		||||
          1002,
 | 
			
		||||
          'WS_ERR_EXPECTED_FIN'
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (compressed) {
 | 
			
		||||
        this._loop = false;
 | 
			
		||||
        return error(
 | 
			
		||||
          RangeError,
 | 
			
		||||
          'RSV1 must be clear',
 | 
			
		||||
          true,
 | 
			
		||||
          1002,
 | 
			
		||||
          'WS_ERR_UNEXPECTED_RSV_1'
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (
 | 
			
		||||
        this._payloadLength > 0x7d ||
 | 
			
		||||
        (this._opcode === 0x08 && this._payloadLength === 1)
 | 
			
		||||
      ) {
 | 
			
		||||
        this._loop = false;
 | 
			
		||||
        return error(
 | 
			
		||||
          RangeError,
 | 
			
		||||
          `invalid payload length ${this._payloadLength}`,
 | 
			
		||||
          true,
 | 
			
		||||
          1002,
 | 
			
		||||
          'WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH'
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      this._loop = false;
 | 
			
		||||
      return error(
 | 
			
		||||
        RangeError,
 | 
			
		||||
        `invalid opcode ${this._opcode}`,
 | 
			
		||||
        true,
 | 
			
		||||
        1002,
 | 
			
		||||
        'WS_ERR_INVALID_OPCODE'
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!this._fin && !this._fragmented) this._fragmented = this._opcode;
 | 
			
		||||
    this._masked = (buf[1] & 0x80) === 0x80;
 | 
			
		||||
 | 
			
		||||
    if (this._isServer) {
 | 
			
		||||
      if (!this._masked) {
 | 
			
		||||
        this._loop = false;
 | 
			
		||||
        return error(
 | 
			
		||||
          RangeError,
 | 
			
		||||
          'MASK must be set',
 | 
			
		||||
          true,
 | 
			
		||||
          1002,
 | 
			
		||||
          'WS_ERR_EXPECTED_MASK'
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
    } else if (this._masked) {
 | 
			
		||||
      this._loop = false;
 | 
			
		||||
      return error(
 | 
			
		||||
        RangeError,
 | 
			
		||||
        'MASK must be clear',
 | 
			
		||||
        true,
 | 
			
		||||
        1002,
 | 
			
		||||
        'WS_ERR_UNEXPECTED_MASK'
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16;
 | 
			
		||||
    else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64;
 | 
			
		||||
    else return this.haveLength();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Gets extended payload length (7+16).
 | 
			
		||||
   *
 | 
			
		||||
   * @return {(RangeError|undefined)} A possible error
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  getPayloadLength16() {
 | 
			
		||||
    if (this._bufferedBytes < 2) {
 | 
			
		||||
      this._loop = false;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this._payloadLength = this.consume(2).readUInt16BE(0);
 | 
			
		||||
    return this.haveLength();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Gets extended payload length (7+64).
 | 
			
		||||
   *
 | 
			
		||||
   * @return {(RangeError|undefined)} A possible error
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  getPayloadLength64() {
 | 
			
		||||
    if (this._bufferedBytes < 8) {
 | 
			
		||||
      this._loop = false;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const buf = this.consume(8);
 | 
			
		||||
    const num = buf.readUInt32BE(0);
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // The maximum safe integer in JavaScript is 2^53 - 1. An error is returned
 | 
			
		||||
    // if payload length is greater than this number.
 | 
			
		||||
    //
 | 
			
		||||
    if (num > Math.pow(2, 53 - 32) - 1) {
 | 
			
		||||
      this._loop = false;
 | 
			
		||||
      return error(
 | 
			
		||||
        RangeError,
 | 
			
		||||
        'Unsupported WebSocket frame: payload length > 2^53 - 1',
 | 
			
		||||
        false,
 | 
			
		||||
        1009,
 | 
			
		||||
        'WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH'
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4);
 | 
			
		||||
    return this.haveLength();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Payload length has been read.
 | 
			
		||||
   *
 | 
			
		||||
   * @return {(RangeError|undefined)} A possible error
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  haveLength() {
 | 
			
		||||
    if (this._payloadLength && this._opcode < 0x08) {
 | 
			
		||||
      this._totalPayloadLength += this._payloadLength;
 | 
			
		||||
      if (this._totalPayloadLength > this._maxPayload && this._maxPayload > 0) {
 | 
			
		||||
        this._loop = false;
 | 
			
		||||
        return error(
 | 
			
		||||
          RangeError,
 | 
			
		||||
          'Max payload size exceeded',
 | 
			
		||||
          false,
 | 
			
		||||
          1009,
 | 
			
		||||
          'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this._masked) this._state = GET_MASK;
 | 
			
		||||
    else this._state = GET_DATA;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Reads mask bytes.
 | 
			
		||||
   *
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  getMask() {
 | 
			
		||||
    if (this._bufferedBytes < 4) {
 | 
			
		||||
      this._loop = false;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this._mask = this.consume(4);
 | 
			
		||||
    this._state = GET_DATA;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Reads data bytes.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Function} cb Callback
 | 
			
		||||
   * @return {(Error|RangeError|undefined)} A possible error
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  getData(cb) {
 | 
			
		||||
    let data = EMPTY_BUFFER;
 | 
			
		||||
 | 
			
		||||
    if (this._payloadLength) {
 | 
			
		||||
      if (this._bufferedBytes < this._payloadLength) {
 | 
			
		||||
        this._loop = false;
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      data = this.consume(this._payloadLength);
 | 
			
		||||
 | 
			
		||||
      if (
 | 
			
		||||
        this._masked &&
 | 
			
		||||
        (this._mask[0] | this._mask[1] | this._mask[2] | this._mask[3]) !== 0
 | 
			
		||||
      ) {
 | 
			
		||||
        unmask(data, this._mask);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this._opcode > 0x07) return this.controlMessage(data);
 | 
			
		||||
 | 
			
		||||
    if (this._compressed) {
 | 
			
		||||
      this._state = INFLATING;
 | 
			
		||||
      this.decompress(data, cb);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (data.length) {
 | 
			
		||||
      //
 | 
			
		||||
      // This message is not compressed so its length is the sum of the payload
 | 
			
		||||
      // length of all fragments.
 | 
			
		||||
      //
 | 
			
		||||
      this._messageLength = this._totalPayloadLength;
 | 
			
		||||
      this._fragments.push(data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return this.dataMessage();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Decompresses data.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Buffer} data Compressed data
 | 
			
		||||
   * @param {Function} cb Callback
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  decompress(data, cb) {
 | 
			
		||||
    const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
 | 
			
		||||
 | 
			
		||||
    perMessageDeflate.decompress(data, this._fin, (err, buf) => {
 | 
			
		||||
      if (err) return cb(err);
 | 
			
		||||
 | 
			
		||||
      if (buf.length) {
 | 
			
		||||
        this._messageLength += buf.length;
 | 
			
		||||
        if (this._messageLength > this._maxPayload && this._maxPayload > 0) {
 | 
			
		||||
          return cb(
 | 
			
		||||
            error(
 | 
			
		||||
              RangeError,
 | 
			
		||||
              'Max payload size exceeded',
 | 
			
		||||
              false,
 | 
			
		||||
              1009,
 | 
			
		||||
              'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'
 | 
			
		||||
            )
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._fragments.push(buf);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const er = this.dataMessage();
 | 
			
		||||
      if (er) return cb(er);
 | 
			
		||||
 | 
			
		||||
      this.startLoop(cb);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Handles a data message.
 | 
			
		||||
   *
 | 
			
		||||
   * @return {(Error|undefined)} A possible error
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  dataMessage() {
 | 
			
		||||
    if (this._fin) {
 | 
			
		||||
      const messageLength = this._messageLength;
 | 
			
		||||
      const fragments = this._fragments;
 | 
			
		||||
 | 
			
		||||
      this._totalPayloadLength = 0;
 | 
			
		||||
      this._messageLength = 0;
 | 
			
		||||
      this._fragmented = 0;
 | 
			
		||||
      this._fragments = [];
 | 
			
		||||
 | 
			
		||||
      if (this._opcode === 2) {
 | 
			
		||||
        let data;
 | 
			
		||||
 | 
			
		||||
        if (this._binaryType === 'nodebuffer') {
 | 
			
		||||
          data = concat(fragments, messageLength);
 | 
			
		||||
        } else if (this._binaryType === 'arraybuffer') {
 | 
			
		||||
          data = toArrayBuffer(concat(fragments, messageLength));
 | 
			
		||||
        } else {
 | 
			
		||||
          data = fragments;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.emit('message', data, true);
 | 
			
		||||
      } else {
 | 
			
		||||
        const buf = concat(fragments, messageLength);
 | 
			
		||||
 | 
			
		||||
        if (!this._skipUTF8Validation && !isValidUTF8(buf)) {
 | 
			
		||||
          this._loop = false;
 | 
			
		||||
          return error(
 | 
			
		||||
            Error,
 | 
			
		||||
            'invalid UTF-8 sequence',
 | 
			
		||||
            true,
 | 
			
		||||
            1007,
 | 
			
		||||
            'WS_ERR_INVALID_UTF8'
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.emit('message', buf, false);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this._state = GET_INFO;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Handles a control message.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Buffer} data Data to handle
 | 
			
		||||
   * @return {(Error|RangeError|undefined)} A possible error
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  controlMessage(data) {
 | 
			
		||||
    if (this._opcode === 0x08) {
 | 
			
		||||
      this._loop = false;
 | 
			
		||||
 | 
			
		||||
      if (data.length === 0) {
 | 
			
		||||
        this.emit('conclude', 1005, EMPTY_BUFFER);
 | 
			
		||||
        this.end();
 | 
			
		||||
      } else {
 | 
			
		||||
        const code = data.readUInt16BE(0);
 | 
			
		||||
 | 
			
		||||
        if (!isValidStatusCode(code)) {
 | 
			
		||||
          return error(
 | 
			
		||||
            RangeError,
 | 
			
		||||
            `invalid status code ${code}`,
 | 
			
		||||
            true,
 | 
			
		||||
            1002,
 | 
			
		||||
            'WS_ERR_INVALID_CLOSE_CODE'
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const buf = new FastBuffer(
 | 
			
		||||
          data.buffer,
 | 
			
		||||
          data.byteOffset + 2,
 | 
			
		||||
          data.length - 2
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if (!this._skipUTF8Validation && !isValidUTF8(buf)) {
 | 
			
		||||
          return error(
 | 
			
		||||
            Error,
 | 
			
		||||
            'invalid UTF-8 sequence',
 | 
			
		||||
            true,
 | 
			
		||||
            1007,
 | 
			
		||||
            'WS_ERR_INVALID_UTF8'
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.emit('conclude', code, buf);
 | 
			
		||||
        this.end();
 | 
			
		||||
      }
 | 
			
		||||
    } else if (this._opcode === 0x09) {
 | 
			
		||||
      this.emit('ping', data);
 | 
			
		||||
    } else {
 | 
			
		||||
      this.emit('pong', data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this._state = GET_INFO;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = Receiver;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Builds an error object.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {function(new:Error|RangeError)} ErrorCtor The error constructor
 | 
			
		||||
 * @param {String} message The error message
 | 
			
		||||
 * @param {Boolean} prefix Specifies whether or not to add a default prefix to
 | 
			
		||||
 *     `message`
 | 
			
		||||
 * @param {Number} statusCode The status code
 | 
			
		||||
 * @param {String} errorCode The exposed error code
 | 
			
		||||
 * @return {(Error|RangeError)} The error
 | 
			
		||||
 * @private
 | 
			
		||||
 */
 | 
			
		||||
function error(ErrorCtor, message, prefix, statusCode, errorCode) {
 | 
			
		||||
  const err = new ErrorCtor(
 | 
			
		||||
    prefix ? `Invalid WebSocket frame: ${message}` : message
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  Error.captureStackTrace(err, error);
 | 
			
		||||
  err.code = errorCode;
 | 
			
		||||
  err[kStatusCode] = statusCode;
 | 
			
		||||
  return err;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										478
									
								
								node_modules/ws/lib/sender.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										478
									
								
								node_modules/ws/lib/sender.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,478 @@
 | 
			
		||||
/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^net|tls$" }] */
 | 
			
		||||
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
const net = require('net');
 | 
			
		||||
const tls = require('tls');
 | 
			
		||||
const { randomFillSync } = require('crypto');
 | 
			
		||||
 | 
			
		||||
const PerMessageDeflate = require('./permessage-deflate');
 | 
			
		||||
const { EMPTY_BUFFER } = require('./constants');
 | 
			
		||||
const { isValidStatusCode } = require('./validation');
 | 
			
		||||
const { mask: applyMask, toBuffer } = require('./buffer-util');
 | 
			
		||||
 | 
			
		||||
const kByteLength = Symbol('kByteLength');
 | 
			
		||||
const maskBuffer = Buffer.alloc(4);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * HyBi Sender implementation.
 | 
			
		||||
 */
 | 
			
		||||
class Sender {
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates a Sender instance.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {(net.Socket|tls.Socket)} socket The connection socket
 | 
			
		||||
   * @param {Object} [extensions] An object containing the negotiated extensions
 | 
			
		||||
   * @param {Function} [generateMask] The function used to generate the masking
 | 
			
		||||
   *     key
 | 
			
		||||
   */
 | 
			
		||||
  constructor(socket, extensions, generateMask) {
 | 
			
		||||
    this._extensions = extensions || {};
 | 
			
		||||
 | 
			
		||||
    if (generateMask) {
 | 
			
		||||
      this._generateMask = generateMask;
 | 
			
		||||
      this._maskBuffer = Buffer.alloc(4);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this._socket = socket;
 | 
			
		||||
 | 
			
		||||
    this._firstFragment = true;
 | 
			
		||||
    this._compress = false;
 | 
			
		||||
 | 
			
		||||
    this._bufferedBytes = 0;
 | 
			
		||||
    this._deflating = false;
 | 
			
		||||
    this._queue = [];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Frames a piece of data according to the HyBi WebSocket protocol.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {(Buffer|String)} data The data to frame
 | 
			
		||||
   * @param {Object} options Options object
 | 
			
		||||
   * @param {Boolean} [options.fin=false] Specifies whether or not to set the
 | 
			
		||||
   *     FIN bit
 | 
			
		||||
   * @param {Function} [options.generateMask] The function used to generate the
 | 
			
		||||
   *     masking key
 | 
			
		||||
   * @param {Boolean} [options.mask=false] Specifies whether or not to mask
 | 
			
		||||
   *     `data`
 | 
			
		||||
   * @param {Buffer} [options.maskBuffer] The buffer used to store the masking
 | 
			
		||||
   *     key
 | 
			
		||||
   * @param {Number} options.opcode The opcode
 | 
			
		||||
   * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
 | 
			
		||||
   *     modified
 | 
			
		||||
   * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
 | 
			
		||||
   *     RSV1 bit
 | 
			
		||||
   * @return {(Buffer|String)[]} The framed data
 | 
			
		||||
   * @public
 | 
			
		||||
   */
 | 
			
		||||
  static frame(data, options) {
 | 
			
		||||
    let mask;
 | 
			
		||||
    let merge = false;
 | 
			
		||||
    let offset = 2;
 | 
			
		||||
    let skipMasking = false;
 | 
			
		||||
 | 
			
		||||
    if (options.mask) {
 | 
			
		||||
      mask = options.maskBuffer || maskBuffer;
 | 
			
		||||
 | 
			
		||||
      if (options.generateMask) {
 | 
			
		||||
        options.generateMask(mask);
 | 
			
		||||
      } else {
 | 
			
		||||
        randomFillSync(mask, 0, 4);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      skipMasking = (mask[0] | mask[1] | mask[2] | mask[3]) === 0;
 | 
			
		||||
      offset = 6;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let dataLength;
 | 
			
		||||
 | 
			
		||||
    if (typeof data === 'string') {
 | 
			
		||||
      if (
 | 
			
		||||
        (!options.mask || skipMasking) &&
 | 
			
		||||
        options[kByteLength] !== undefined
 | 
			
		||||
      ) {
 | 
			
		||||
        dataLength = options[kByteLength];
 | 
			
		||||
      } else {
 | 
			
		||||
        data = Buffer.from(data);
 | 
			
		||||
        dataLength = data.length;
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      dataLength = data.length;
 | 
			
		||||
      merge = options.mask && options.readOnly && !skipMasking;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let payloadLength = dataLength;
 | 
			
		||||
 | 
			
		||||
    if (dataLength >= 65536) {
 | 
			
		||||
      offset += 8;
 | 
			
		||||
      payloadLength = 127;
 | 
			
		||||
    } else if (dataLength > 125) {
 | 
			
		||||
      offset += 2;
 | 
			
		||||
      payloadLength = 126;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const target = Buffer.allocUnsafe(merge ? dataLength + offset : offset);
 | 
			
		||||
 | 
			
		||||
    target[0] = options.fin ? options.opcode | 0x80 : options.opcode;
 | 
			
		||||
    if (options.rsv1) target[0] |= 0x40;
 | 
			
		||||
 | 
			
		||||
    target[1] = payloadLength;
 | 
			
		||||
 | 
			
		||||
    if (payloadLength === 126) {
 | 
			
		||||
      target.writeUInt16BE(dataLength, 2);
 | 
			
		||||
    } else if (payloadLength === 127) {
 | 
			
		||||
      target[2] = target[3] = 0;
 | 
			
		||||
      target.writeUIntBE(dataLength, 4, 6);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!options.mask) return [target, data];
 | 
			
		||||
 | 
			
		||||
    target[1] |= 0x80;
 | 
			
		||||
    target[offset - 4] = mask[0];
 | 
			
		||||
    target[offset - 3] = mask[1];
 | 
			
		||||
    target[offset - 2] = mask[2];
 | 
			
		||||
    target[offset - 1] = mask[3];
 | 
			
		||||
 | 
			
		||||
    if (skipMasking) return [target, data];
 | 
			
		||||
 | 
			
		||||
    if (merge) {
 | 
			
		||||
      applyMask(data, mask, target, offset, dataLength);
 | 
			
		||||
      return [target];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    applyMask(data, mask, data, 0, dataLength);
 | 
			
		||||
    return [target, data];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Sends a close message to the other peer.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Number} [code] The status code component of the body
 | 
			
		||||
   * @param {(String|Buffer)} [data] The message component of the body
 | 
			
		||||
   * @param {Boolean} [mask=false] Specifies whether or not to mask the message
 | 
			
		||||
   * @param {Function} [cb] Callback
 | 
			
		||||
   * @public
 | 
			
		||||
   */
 | 
			
		||||
  close(code, data, mask, cb) {
 | 
			
		||||
    let buf;
 | 
			
		||||
 | 
			
		||||
    if (code === undefined) {
 | 
			
		||||
      buf = EMPTY_BUFFER;
 | 
			
		||||
    } else if (typeof code !== 'number' || !isValidStatusCode(code)) {
 | 
			
		||||
      throw new TypeError('First argument must be a valid error code number');
 | 
			
		||||
    } else if (data === undefined || !data.length) {
 | 
			
		||||
      buf = Buffer.allocUnsafe(2);
 | 
			
		||||
      buf.writeUInt16BE(code, 0);
 | 
			
		||||
    } else {
 | 
			
		||||
      const length = Buffer.byteLength(data);
 | 
			
		||||
 | 
			
		||||
      if (length > 123) {
 | 
			
		||||
        throw new RangeError('The message must not be greater than 123 bytes');
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      buf = Buffer.allocUnsafe(2 + length);
 | 
			
		||||
      buf.writeUInt16BE(code, 0);
 | 
			
		||||
 | 
			
		||||
      if (typeof data === 'string') {
 | 
			
		||||
        buf.write(data, 2);
 | 
			
		||||
      } else {
 | 
			
		||||
        buf.set(data, 2);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const options = {
 | 
			
		||||
      [kByteLength]: buf.length,
 | 
			
		||||
      fin: true,
 | 
			
		||||
      generateMask: this._generateMask,
 | 
			
		||||
      mask,
 | 
			
		||||
      maskBuffer: this._maskBuffer,
 | 
			
		||||
      opcode: 0x08,
 | 
			
		||||
      readOnly: false,
 | 
			
		||||
      rsv1: false
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (this._deflating) {
 | 
			
		||||
      this.enqueue([this.dispatch, buf, false, options, cb]);
 | 
			
		||||
    } else {
 | 
			
		||||
      this.sendFrame(Sender.frame(buf, options), cb);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Sends a ping message to the other peer.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {*} data The message to send
 | 
			
		||||
   * @param {Boolean} [mask=false] Specifies whether or not to mask `data`
 | 
			
		||||
   * @param {Function} [cb] Callback
 | 
			
		||||
   * @public
 | 
			
		||||
   */
 | 
			
		||||
  ping(data, mask, cb) {
 | 
			
		||||
    let byteLength;
 | 
			
		||||
    let readOnly;
 | 
			
		||||
 | 
			
		||||
    if (typeof data === 'string') {
 | 
			
		||||
      byteLength = Buffer.byteLength(data);
 | 
			
		||||
      readOnly = false;
 | 
			
		||||
    } else {
 | 
			
		||||
      data = toBuffer(data);
 | 
			
		||||
      byteLength = data.length;
 | 
			
		||||
      readOnly = toBuffer.readOnly;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (byteLength > 125) {
 | 
			
		||||
      throw new RangeError('The data size must not be greater than 125 bytes');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const options = {
 | 
			
		||||
      [kByteLength]: byteLength,
 | 
			
		||||
      fin: true,
 | 
			
		||||
      generateMask: this._generateMask,
 | 
			
		||||
      mask,
 | 
			
		||||
      maskBuffer: this._maskBuffer,
 | 
			
		||||
      opcode: 0x09,
 | 
			
		||||
      readOnly,
 | 
			
		||||
      rsv1: false
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (this._deflating) {
 | 
			
		||||
      this.enqueue([this.dispatch, data, false, options, cb]);
 | 
			
		||||
    } else {
 | 
			
		||||
      this.sendFrame(Sender.frame(data, options), cb);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Sends a pong message to the other peer.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {*} data The message to send
 | 
			
		||||
   * @param {Boolean} [mask=false] Specifies whether or not to mask `data`
 | 
			
		||||
   * @param {Function} [cb] Callback
 | 
			
		||||
   * @public
 | 
			
		||||
   */
 | 
			
		||||
  pong(data, mask, cb) {
 | 
			
		||||
    let byteLength;
 | 
			
		||||
    let readOnly;
 | 
			
		||||
 | 
			
		||||
    if (typeof data === 'string') {
 | 
			
		||||
      byteLength = Buffer.byteLength(data);
 | 
			
		||||
      readOnly = false;
 | 
			
		||||
    } else {
 | 
			
		||||
      data = toBuffer(data);
 | 
			
		||||
      byteLength = data.length;
 | 
			
		||||
      readOnly = toBuffer.readOnly;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (byteLength > 125) {
 | 
			
		||||
      throw new RangeError('The data size must not be greater than 125 bytes');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const options = {
 | 
			
		||||
      [kByteLength]: byteLength,
 | 
			
		||||
      fin: true,
 | 
			
		||||
      generateMask: this._generateMask,
 | 
			
		||||
      mask,
 | 
			
		||||
      maskBuffer: this._maskBuffer,
 | 
			
		||||
      opcode: 0x0a,
 | 
			
		||||
      readOnly,
 | 
			
		||||
      rsv1: false
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (this._deflating) {
 | 
			
		||||
      this.enqueue([this.dispatch, data, false, options, cb]);
 | 
			
		||||
    } else {
 | 
			
		||||
      this.sendFrame(Sender.frame(data, options), cb);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Sends a data message to the other peer.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {*} data The message to send
 | 
			
		||||
   * @param {Object} options Options object
 | 
			
		||||
   * @param {Boolean} [options.binary=false] Specifies whether `data` is binary
 | 
			
		||||
   *     or text
 | 
			
		||||
   * @param {Boolean} [options.compress=false] Specifies whether or not to
 | 
			
		||||
   *     compress `data`
 | 
			
		||||
   * @param {Boolean} [options.fin=false] Specifies whether the fragment is the
 | 
			
		||||
   *     last one
 | 
			
		||||
   * @param {Boolean} [options.mask=false] Specifies whether or not to mask
 | 
			
		||||
   *     `data`
 | 
			
		||||
   * @param {Function} [cb] Callback
 | 
			
		||||
   * @public
 | 
			
		||||
   */
 | 
			
		||||
  send(data, options, cb) {
 | 
			
		||||
    const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
 | 
			
		||||
    let opcode = options.binary ? 2 : 1;
 | 
			
		||||
    let rsv1 = options.compress;
 | 
			
		||||
 | 
			
		||||
    let byteLength;
 | 
			
		||||
    let readOnly;
 | 
			
		||||
 | 
			
		||||
    if (typeof data === 'string') {
 | 
			
		||||
      byteLength = Buffer.byteLength(data);
 | 
			
		||||
      readOnly = false;
 | 
			
		||||
    } else {
 | 
			
		||||
      data = toBuffer(data);
 | 
			
		||||
      byteLength = data.length;
 | 
			
		||||
      readOnly = toBuffer.readOnly;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this._firstFragment) {
 | 
			
		||||
      this._firstFragment = false;
 | 
			
		||||
      if (
 | 
			
		||||
        rsv1 &&
 | 
			
		||||
        perMessageDeflate &&
 | 
			
		||||
        perMessageDeflate.params[
 | 
			
		||||
          perMessageDeflate._isServer
 | 
			
		||||
            ? 'server_no_context_takeover'
 | 
			
		||||
            : 'client_no_context_takeover'
 | 
			
		||||
        ]
 | 
			
		||||
      ) {
 | 
			
		||||
        rsv1 = byteLength >= perMessageDeflate._threshold;
 | 
			
		||||
      }
 | 
			
		||||
      this._compress = rsv1;
 | 
			
		||||
    } else {
 | 
			
		||||
      rsv1 = false;
 | 
			
		||||
      opcode = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (options.fin) this._firstFragment = true;
 | 
			
		||||
 | 
			
		||||
    if (perMessageDeflate) {
 | 
			
		||||
      const opts = {
 | 
			
		||||
        [kByteLength]: byteLength,
 | 
			
		||||
        fin: options.fin,
 | 
			
		||||
        generateMask: this._generateMask,
 | 
			
		||||
        mask: options.mask,
 | 
			
		||||
        maskBuffer: this._maskBuffer,
 | 
			
		||||
        opcode,
 | 
			
		||||
        readOnly,
 | 
			
		||||
        rsv1
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      if (this._deflating) {
 | 
			
		||||
        this.enqueue([this.dispatch, data, this._compress, opts, cb]);
 | 
			
		||||
      } else {
 | 
			
		||||
        this.dispatch(data, this._compress, opts, cb);
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      this.sendFrame(
 | 
			
		||||
        Sender.frame(data, {
 | 
			
		||||
          [kByteLength]: byteLength,
 | 
			
		||||
          fin: options.fin,
 | 
			
		||||
          generateMask: this._generateMask,
 | 
			
		||||
          mask: options.mask,
 | 
			
		||||
          maskBuffer: this._maskBuffer,
 | 
			
		||||
          opcode,
 | 
			
		||||
          readOnly,
 | 
			
		||||
          rsv1: false
 | 
			
		||||
        }),
 | 
			
		||||
        cb
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Dispatches a message.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {(Buffer|String)} data The message to send
 | 
			
		||||
   * @param {Boolean} [compress=false] Specifies whether or not to compress
 | 
			
		||||
   *     `data`
 | 
			
		||||
   * @param {Object} options Options object
 | 
			
		||||
   * @param {Boolean} [options.fin=false] Specifies whether or not to set the
 | 
			
		||||
   *     FIN bit
 | 
			
		||||
   * @param {Function} [options.generateMask] The function used to generate the
 | 
			
		||||
   *     masking key
 | 
			
		||||
   * @param {Boolean} [options.mask=false] Specifies whether or not to mask
 | 
			
		||||
   *     `data`
 | 
			
		||||
   * @param {Buffer} [options.maskBuffer] The buffer used to store the masking
 | 
			
		||||
   *     key
 | 
			
		||||
   * @param {Number} options.opcode The opcode
 | 
			
		||||
   * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
 | 
			
		||||
   *     modified
 | 
			
		||||
   * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
 | 
			
		||||
   *     RSV1 bit
 | 
			
		||||
   * @param {Function} [cb] Callback
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  dispatch(data, compress, options, cb) {
 | 
			
		||||
    if (!compress) {
 | 
			
		||||
      this.sendFrame(Sender.frame(data, options), cb);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
 | 
			
		||||
 | 
			
		||||
    this._bufferedBytes += options[kByteLength];
 | 
			
		||||
    this._deflating = true;
 | 
			
		||||
    perMessageDeflate.compress(data, options.fin, (_, buf) => {
 | 
			
		||||
      if (this._socket.destroyed) {
 | 
			
		||||
        const err = new Error(
 | 
			
		||||
          'The socket was closed while data was being compressed'
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if (typeof cb === 'function') cb(err);
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._queue.length; i++) {
 | 
			
		||||
          const params = this._queue[i];
 | 
			
		||||
          const callback = params[params.length - 1];
 | 
			
		||||
 | 
			
		||||
          if (typeof callback === 'function') callback(err);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      this._bufferedBytes -= options[kByteLength];
 | 
			
		||||
      this._deflating = false;
 | 
			
		||||
      options.readOnly = false;
 | 
			
		||||
      this.sendFrame(Sender.frame(buf, options), cb);
 | 
			
		||||
      this.dequeue();
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Executes queued send operations.
 | 
			
		||||
   *
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  dequeue() {
 | 
			
		||||
    while (!this._deflating && this._queue.length) {
 | 
			
		||||
      const params = this._queue.shift();
 | 
			
		||||
 | 
			
		||||
      this._bufferedBytes -= params[3][kByteLength];
 | 
			
		||||
      Reflect.apply(params[0], this, params.slice(1));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Enqueues a send operation.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Array} params Send operation parameters.
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  enqueue(params) {
 | 
			
		||||
    this._bufferedBytes += params[3][kByteLength];
 | 
			
		||||
    this._queue.push(params);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Sends a frame.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Buffer[]} list The frame to send
 | 
			
		||||
   * @param {Function} [cb] Callback
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  sendFrame(list, cb) {
 | 
			
		||||
    if (list.length === 2) {
 | 
			
		||||
      this._socket.cork();
 | 
			
		||||
      this._socket.write(list[0]);
 | 
			
		||||
      this._socket.write(list[1], cb);
 | 
			
		||||
      this._socket.uncork();
 | 
			
		||||
    } else {
 | 
			
		||||
      this._socket.write(list[0], cb);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = Sender;
 | 
			
		||||
							
								
								
									
										159
									
								
								node_modules/ws/lib/stream.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								node_modules/ws/lib/stream.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,159 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
const { Duplex } = require('stream');
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Emits the `'close'` event on a stream.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {Duplex} stream The stream.
 | 
			
		||||
 * @private
 | 
			
		||||
 */
 | 
			
		||||
function emitClose(stream) {
 | 
			
		||||
  stream.emit('close');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The listener of the `'end'` event.
 | 
			
		||||
 *
 | 
			
		||||
 * @private
 | 
			
		||||
 */
 | 
			
		||||
function duplexOnEnd() {
 | 
			
		||||
  if (!this.destroyed && this._writableState.finished) {
 | 
			
		||||
    this.destroy();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The listener of the `'error'` event.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {Error} err The error
 | 
			
		||||
 * @private
 | 
			
		||||
 */
 | 
			
		||||
function duplexOnError(err) {
 | 
			
		||||
  this.removeListener('error', duplexOnError);
 | 
			
		||||
  this.destroy();
 | 
			
		||||
  if (this.listenerCount('error') === 0) {
 | 
			
		||||
    // Do not suppress the throwing behavior.
 | 
			
		||||
    this.emit('error', err);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Wraps a `WebSocket` in a duplex stream.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {WebSocket} ws The `WebSocket` to wrap
 | 
			
		||||
 * @param {Object} [options] The options for the `Duplex` constructor
 | 
			
		||||
 * @return {Duplex} The duplex stream
 | 
			
		||||
 * @public
 | 
			
		||||
 */
 | 
			
		||||
function createWebSocketStream(ws, options) {
 | 
			
		||||
  let terminateOnDestroy = true;
 | 
			
		||||
 | 
			
		||||
  const duplex = new Duplex({
 | 
			
		||||
    ...options,
 | 
			
		||||
    autoDestroy: false,
 | 
			
		||||
    emitClose: false,
 | 
			
		||||
    objectMode: false,
 | 
			
		||||
    writableObjectMode: false
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  ws.on('message', function message(msg, isBinary) {
 | 
			
		||||
    const data =
 | 
			
		||||
      !isBinary && duplex._readableState.objectMode ? msg.toString() : msg;
 | 
			
		||||
 | 
			
		||||
    if (!duplex.push(data)) ws.pause();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  ws.once('error', function error(err) {
 | 
			
		||||
    if (duplex.destroyed) return;
 | 
			
		||||
 | 
			
		||||
    // Prevent `ws.terminate()` from being called by `duplex._destroy()`.
 | 
			
		||||
    //
 | 
			
		||||
    // - If the `'error'` event is emitted before the `'open'` event, then
 | 
			
		||||
    //   `ws.terminate()` is a noop as no socket is assigned.
 | 
			
		||||
    // - Otherwise, the error is re-emitted by the listener of the `'error'`
 | 
			
		||||
    //   event of the `Receiver` object. The listener already closes the
 | 
			
		||||
    //   connection by calling `ws.close()`. This allows a close frame to be
 | 
			
		||||
    //   sent to the other peer. If `ws.terminate()` is called right after this,
 | 
			
		||||
    //   then the close frame might not be sent.
 | 
			
		||||
    terminateOnDestroy = false;
 | 
			
		||||
    duplex.destroy(err);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  ws.once('close', function close() {
 | 
			
		||||
    if (duplex.destroyed) return;
 | 
			
		||||
 | 
			
		||||
    duplex.push(null);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  duplex._destroy = function (err, callback) {
 | 
			
		||||
    if (ws.readyState === ws.CLOSED) {
 | 
			
		||||
      callback(err);
 | 
			
		||||
      process.nextTick(emitClose, duplex);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let called = false;
 | 
			
		||||
 | 
			
		||||
    ws.once('error', function error(err) {
 | 
			
		||||
      called = true;
 | 
			
		||||
      callback(err);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    ws.once('close', function close() {
 | 
			
		||||
      if (!called) callback(err);
 | 
			
		||||
      process.nextTick(emitClose, duplex);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (terminateOnDestroy) ws.terminate();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  duplex._final = function (callback) {
 | 
			
		||||
    if (ws.readyState === ws.CONNECTING) {
 | 
			
		||||
      ws.once('open', function open() {
 | 
			
		||||
        duplex._final(callback);
 | 
			
		||||
      });
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // If the value of the `_socket` property is `null` it means that `ws` is a
 | 
			
		||||
    // client websocket and the handshake failed. In fact, when this happens, a
 | 
			
		||||
    // socket is never assigned to the websocket. Wait for the `'error'` event
 | 
			
		||||
    // that will be emitted by the websocket.
 | 
			
		||||
    if (ws._socket === null) return;
 | 
			
		||||
 | 
			
		||||
    if (ws._socket._writableState.finished) {
 | 
			
		||||
      callback();
 | 
			
		||||
      if (duplex._readableState.endEmitted) duplex.destroy();
 | 
			
		||||
    } else {
 | 
			
		||||
      ws._socket.once('finish', function finish() {
 | 
			
		||||
        // `duplex` is not destroyed here because the `'end'` event will be
 | 
			
		||||
        // emitted on `duplex` after this `'finish'` event. The EOF signaling
 | 
			
		||||
        // `null` chunk is, in fact, pushed when the websocket emits `'close'`.
 | 
			
		||||
        callback();
 | 
			
		||||
      });
 | 
			
		||||
      ws.close();
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  duplex._read = function () {
 | 
			
		||||
    if (ws.isPaused) ws.resume();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  duplex._write = function (chunk, encoding, callback) {
 | 
			
		||||
    if (ws.readyState === ws.CONNECTING) {
 | 
			
		||||
      ws.once('open', function open() {
 | 
			
		||||
        duplex._write(chunk, encoding, callback);
 | 
			
		||||
      });
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ws.send(chunk, callback);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  duplex.on('end', duplexOnEnd);
 | 
			
		||||
  duplex.on('error', duplexOnError);
 | 
			
		||||
  return duplex;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = createWebSocketStream;
 | 
			
		||||
							
								
								
									
										62
									
								
								node_modules/ws/lib/subprotocol.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								node_modules/ws/lib/subprotocol.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
const { tokenChars } = require('./validation');
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Parses the `Sec-WebSocket-Protocol` header into a set of subprotocol names.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {String} header The field value of the header
 | 
			
		||||
 * @return {Set} The subprotocol names
 | 
			
		||||
 * @public
 | 
			
		||||
 */
 | 
			
		||||
function parse(header) {
 | 
			
		||||
  const protocols = new Set();
 | 
			
		||||
  let start = -1;
 | 
			
		||||
  let end = -1;
 | 
			
		||||
  let i = 0;
 | 
			
		||||
 | 
			
		||||
  for (i; i < header.length; i++) {
 | 
			
		||||
    const code = header.charCodeAt(i);
 | 
			
		||||
 | 
			
		||||
    if (end === -1 && tokenChars[code] === 1) {
 | 
			
		||||
      if (start === -1) start = i;
 | 
			
		||||
    } else if (
 | 
			
		||||
      i !== 0 &&
 | 
			
		||||
      (code === 0x20 /* ' ' */ || code === 0x09) /* '\t' */
 | 
			
		||||
    ) {
 | 
			
		||||
      if (end === -1 && start !== -1) end = i;
 | 
			
		||||
    } else if (code === 0x2c /* ',' */) {
 | 
			
		||||
      if (start === -1) {
 | 
			
		||||
        throw new SyntaxError(`Unexpected character at index ${i}`);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (end === -1) end = i;
 | 
			
		||||
 | 
			
		||||
      const protocol = header.slice(start, end);
 | 
			
		||||
 | 
			
		||||
      if (protocols.has(protocol)) {
 | 
			
		||||
        throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      protocols.add(protocol);
 | 
			
		||||
      start = end = -1;
 | 
			
		||||
    } else {
 | 
			
		||||
      throw new SyntaxError(`Unexpected character at index ${i}`);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (start === -1 || end !== -1) {
 | 
			
		||||
    throw new SyntaxError('Unexpected end of input');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const protocol = header.slice(start, i);
 | 
			
		||||
 | 
			
		||||
  if (protocols.has(protocol)) {
 | 
			
		||||
    throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protocols.add(protocol);
 | 
			
		||||
  return protocols;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = { parse };
 | 
			
		||||
							
								
								
									
										130
									
								
								node_modules/ws/lib/validation.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								node_modules/ws/lib/validation.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,130 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
const { isUtf8 } = require('buffer');
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// Allowed token characters:
 | 
			
		||||
//
 | 
			
		||||
// '!', '#', '$', '%', '&', ''', '*', '+', '-',
 | 
			
		||||
// '.', 0-9, A-Z, '^', '_', '`', a-z, '|', '~'
 | 
			
		||||
//
 | 
			
		||||
// tokenChars[32] === 0 // ' '
 | 
			
		||||
// tokenChars[33] === 1 // '!'
 | 
			
		||||
// tokenChars[34] === 0 // '"'
 | 
			
		||||
// ...
 | 
			
		||||
//
 | 
			
		||||
// prettier-ignore
 | 
			
		||||
const tokenChars = [
 | 
			
		||||
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15
 | 
			
		||||
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31
 | 
			
		||||
  0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32 - 47
 | 
			
		||||
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63
 | 
			
		||||
  0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79
 | 
			
		||||
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80 - 95
 | 
			
		||||
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111
 | 
			
		||||
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0 // 112 - 127
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Checks if a status code is allowed in a close frame.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {Number} code The status code
 | 
			
		||||
 * @return {Boolean} `true` if the status code is valid, else `false`
 | 
			
		||||
 * @public
 | 
			
		||||
 */
 | 
			
		||||
function isValidStatusCode(code) {
 | 
			
		||||
  return (
 | 
			
		||||
    (code >= 1000 &&
 | 
			
		||||
      code <= 1014 &&
 | 
			
		||||
      code !== 1004 &&
 | 
			
		||||
      code !== 1005 &&
 | 
			
		||||
      code !== 1006) ||
 | 
			
		||||
    (code >= 3000 && code <= 4999)
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Checks if a given buffer contains only correct UTF-8.
 | 
			
		||||
 * Ported from https://www.cl.cam.ac.uk/%7Emgk25/ucs/utf8_check.c by
 | 
			
		||||
 * Markus Kuhn.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {Buffer} buf The buffer to check
 | 
			
		||||
 * @return {Boolean} `true` if `buf` contains only correct UTF-8, else `false`
 | 
			
		||||
 * @public
 | 
			
		||||
 */
 | 
			
		||||
function _isValidUTF8(buf) {
 | 
			
		||||
  const len = buf.length;
 | 
			
		||||
  let i = 0;
 | 
			
		||||
 | 
			
		||||
  while (i < len) {
 | 
			
		||||
    if ((buf[i] & 0x80) === 0) {
 | 
			
		||||
      // 0xxxxxxx
 | 
			
		||||
      i++;
 | 
			
		||||
    } else if ((buf[i] & 0xe0) === 0xc0) {
 | 
			
		||||
      // 110xxxxx 10xxxxxx
 | 
			
		||||
      if (
 | 
			
		||||
        i + 1 === len ||
 | 
			
		||||
        (buf[i + 1] & 0xc0) !== 0x80 ||
 | 
			
		||||
        (buf[i] & 0xfe) === 0xc0 // Overlong
 | 
			
		||||
      ) {
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      i += 2;
 | 
			
		||||
    } else if ((buf[i] & 0xf0) === 0xe0) {
 | 
			
		||||
      // 1110xxxx 10xxxxxx 10xxxxxx
 | 
			
		||||
      if (
 | 
			
		||||
        i + 2 >= len ||
 | 
			
		||||
        (buf[i + 1] & 0xc0) !== 0x80 ||
 | 
			
		||||
        (buf[i + 2] & 0xc0) !== 0x80 ||
 | 
			
		||||
        (buf[i] === 0xe0 && (buf[i + 1] & 0xe0) === 0x80) || // Overlong
 | 
			
		||||
        (buf[i] === 0xed && (buf[i + 1] & 0xe0) === 0xa0) // Surrogate (U+D800 - U+DFFF)
 | 
			
		||||
      ) {
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      i += 3;
 | 
			
		||||
    } else if ((buf[i] & 0xf8) === 0xf0) {
 | 
			
		||||
      // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
 | 
			
		||||
      if (
 | 
			
		||||
        i + 3 >= len ||
 | 
			
		||||
        (buf[i + 1] & 0xc0) !== 0x80 ||
 | 
			
		||||
        (buf[i + 2] & 0xc0) !== 0x80 ||
 | 
			
		||||
        (buf[i + 3] & 0xc0) !== 0x80 ||
 | 
			
		||||
        (buf[i] === 0xf0 && (buf[i + 1] & 0xf0) === 0x80) || // Overlong
 | 
			
		||||
        (buf[i] === 0xf4 && buf[i + 1] > 0x8f) ||
 | 
			
		||||
        buf[i] > 0xf4 // > U+10FFFF
 | 
			
		||||
      ) {
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      i += 4;
 | 
			
		||||
    } else {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
  isValidStatusCode,
 | 
			
		||||
  isValidUTF8: _isValidUTF8,
 | 
			
		||||
  tokenChars
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
if (isUtf8) {
 | 
			
		||||
  module.exports.isValidUTF8 = function (buf) {
 | 
			
		||||
    return buf.length < 24 ? _isValidUTF8(buf) : isUtf8(buf);
 | 
			
		||||
  };
 | 
			
		||||
} /* istanbul ignore else  */ else if (!process.env.WS_NO_UTF_8_VALIDATE) {
 | 
			
		||||
  try {
 | 
			
		||||
    const isValidUTF8 = require('utf-8-validate');
 | 
			
		||||
 | 
			
		||||
    module.exports.isValidUTF8 = function (buf) {
 | 
			
		||||
      return buf.length < 32 ? _isValidUTF8(buf) : isValidUTF8(buf);
 | 
			
		||||
    };
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    // Continue regardless of the error.
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										535
									
								
								node_modules/ws/lib/websocket-server.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										535
									
								
								node_modules/ws/lib/websocket-server.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,535 @@
 | 
			
		||||
/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^net|tls|https$" }] */
 | 
			
		||||
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
const EventEmitter = require('events');
 | 
			
		||||
const http = require('http');
 | 
			
		||||
const https = require('https');
 | 
			
		||||
const net = require('net');
 | 
			
		||||
const tls = require('tls');
 | 
			
		||||
const { createHash } = require('crypto');
 | 
			
		||||
 | 
			
		||||
const extension = require('./extension');
 | 
			
		||||
const PerMessageDeflate = require('./permessage-deflate');
 | 
			
		||||
const subprotocol = require('./subprotocol');
 | 
			
		||||
const WebSocket = require('./websocket');
 | 
			
		||||
const { GUID, kWebSocket } = require('./constants');
 | 
			
		||||
 | 
			
		||||
const keyRegex = /^[+/0-9A-Za-z]{22}==$/;
 | 
			
		||||
 | 
			
		||||
const RUNNING = 0;
 | 
			
		||||
const CLOSING = 1;
 | 
			
		||||
const CLOSED = 2;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class representing a WebSocket server.
 | 
			
		||||
 *
 | 
			
		||||
 * @extends EventEmitter
 | 
			
		||||
 */
 | 
			
		||||
class WebSocketServer extends EventEmitter {
 | 
			
		||||
  /**
 | 
			
		||||
   * Create a `WebSocketServer` instance.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Object} options Configuration options
 | 
			
		||||
   * @param {Number} [options.backlog=511] The maximum length of the queue of
 | 
			
		||||
   *     pending connections
 | 
			
		||||
   * @param {Boolean} [options.clientTracking=true] Specifies whether or not to
 | 
			
		||||
   *     track clients
 | 
			
		||||
   * @param {Function} [options.handleProtocols] A hook to handle protocols
 | 
			
		||||
   * @param {String} [options.host] The hostname where to bind the server
 | 
			
		||||
   * @param {Number} [options.maxPayload=104857600] The maximum allowed message
 | 
			
		||||
   *     size
 | 
			
		||||
   * @param {Boolean} [options.noServer=false] Enable no server mode
 | 
			
		||||
   * @param {String} [options.path] Accept only connections matching this path
 | 
			
		||||
   * @param {(Boolean|Object)} [options.perMessageDeflate=false] Enable/disable
 | 
			
		||||
   *     permessage-deflate
 | 
			
		||||
   * @param {Number} [options.port] The port where to bind the server
 | 
			
		||||
   * @param {(http.Server|https.Server)} [options.server] A pre-created HTTP/S
 | 
			
		||||
   *     server to use
 | 
			
		||||
   * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
 | 
			
		||||
   *     not to skip UTF-8 validation for text and close messages
 | 
			
		||||
   * @param {Function} [options.verifyClient] A hook to reject connections
 | 
			
		||||
   * @param {Function} [options.WebSocket=WebSocket] Specifies the `WebSocket`
 | 
			
		||||
   *     class to use. It must be the `WebSocket` class or class that extends it
 | 
			
		||||
   * @param {Function} [callback] A listener for the `listening` event
 | 
			
		||||
   */
 | 
			
		||||
  constructor(options, callback) {
 | 
			
		||||
    super();
 | 
			
		||||
 | 
			
		||||
    options = {
 | 
			
		||||
      maxPayload: 100 * 1024 * 1024,
 | 
			
		||||
      skipUTF8Validation: false,
 | 
			
		||||
      perMessageDeflate: false,
 | 
			
		||||
      handleProtocols: null,
 | 
			
		||||
      clientTracking: true,
 | 
			
		||||
      verifyClient: null,
 | 
			
		||||
      noServer: false,
 | 
			
		||||
      backlog: null, // use default (511 as implemented in net.js)
 | 
			
		||||
      server: null,
 | 
			
		||||
      host: null,
 | 
			
		||||
      path: null,
 | 
			
		||||
      port: null,
 | 
			
		||||
      WebSocket,
 | 
			
		||||
      ...options
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (
 | 
			
		||||
      (options.port == null && !options.server && !options.noServer) ||
 | 
			
		||||
      (options.port != null && (options.server || options.noServer)) ||
 | 
			
		||||
      (options.server && options.noServer)
 | 
			
		||||
    ) {
 | 
			
		||||
      throw new TypeError(
 | 
			
		||||
        'One and only one of the "port", "server", or "noServer" options ' +
 | 
			
		||||
          'must be specified'
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (options.port != null) {
 | 
			
		||||
      this._server = http.createServer((req, res) => {
 | 
			
		||||
        const body = http.STATUS_CODES[426];
 | 
			
		||||
 | 
			
		||||
        res.writeHead(426, {
 | 
			
		||||
          'Content-Length': body.length,
 | 
			
		||||
          'Content-Type': 'text/plain'
 | 
			
		||||
        });
 | 
			
		||||
        res.end(body);
 | 
			
		||||
      });
 | 
			
		||||
      this._server.listen(
 | 
			
		||||
        options.port,
 | 
			
		||||
        options.host,
 | 
			
		||||
        options.backlog,
 | 
			
		||||
        callback
 | 
			
		||||
      );
 | 
			
		||||
    } else if (options.server) {
 | 
			
		||||
      this._server = options.server;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this._server) {
 | 
			
		||||
      const emitConnection = this.emit.bind(this, 'connection');
 | 
			
		||||
 | 
			
		||||
      this._removeListeners = addListeners(this._server, {
 | 
			
		||||
        listening: this.emit.bind(this, 'listening'),
 | 
			
		||||
        error: this.emit.bind(this, 'error'),
 | 
			
		||||
        upgrade: (req, socket, head) => {
 | 
			
		||||
          this.handleUpgrade(req, socket, head, emitConnection);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (options.perMessageDeflate === true) options.perMessageDeflate = {};
 | 
			
		||||
    if (options.clientTracking) {
 | 
			
		||||
      this.clients = new Set();
 | 
			
		||||
      this._shouldEmitClose = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.options = options;
 | 
			
		||||
    this._state = RUNNING;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Returns the bound address, the address family name, and port of the server
 | 
			
		||||
   * as reported by the operating system if listening on an IP socket.
 | 
			
		||||
   * If the server is listening on a pipe or UNIX domain socket, the name is
 | 
			
		||||
   * returned as a string.
 | 
			
		||||
   *
 | 
			
		||||
   * @return {(Object|String|null)} The address of the server
 | 
			
		||||
   * @public
 | 
			
		||||
   */
 | 
			
		||||
  address() {
 | 
			
		||||
    if (this.options.noServer) {
 | 
			
		||||
      throw new Error('The server is operating in "noServer" mode');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!this._server) return null;
 | 
			
		||||
    return this._server.address();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Stop the server from accepting new connections and emit the `'close'` event
 | 
			
		||||
   * when all existing connections are closed.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Function} [cb] A one-time listener for the `'close'` event
 | 
			
		||||
   * @public
 | 
			
		||||
   */
 | 
			
		||||
  close(cb) {
 | 
			
		||||
    if (this._state === CLOSED) {
 | 
			
		||||
      if (cb) {
 | 
			
		||||
        this.once('close', () => {
 | 
			
		||||
          cb(new Error('The server is not running'));
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      process.nextTick(emitClose, this);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (cb) this.once('close', cb);
 | 
			
		||||
 | 
			
		||||
    if (this._state === CLOSING) return;
 | 
			
		||||
    this._state = CLOSING;
 | 
			
		||||
 | 
			
		||||
    if (this.options.noServer || this.options.server) {
 | 
			
		||||
      if (this._server) {
 | 
			
		||||
        this._removeListeners();
 | 
			
		||||
        this._removeListeners = this._server = null;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (this.clients) {
 | 
			
		||||
        if (!this.clients.size) {
 | 
			
		||||
          process.nextTick(emitClose, this);
 | 
			
		||||
        } else {
 | 
			
		||||
          this._shouldEmitClose = true;
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        process.nextTick(emitClose, this);
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      const server = this._server;
 | 
			
		||||
 | 
			
		||||
      this._removeListeners();
 | 
			
		||||
      this._removeListeners = this._server = null;
 | 
			
		||||
 | 
			
		||||
      //
 | 
			
		||||
      // The HTTP/S server was created internally. Close it, and rely on its
 | 
			
		||||
      // `'close'` event.
 | 
			
		||||
      //
 | 
			
		||||
      server.close(() => {
 | 
			
		||||
        emitClose(this);
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * See if a given request should be handled by this server instance.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {http.IncomingMessage} req Request object to inspect
 | 
			
		||||
   * @return {Boolean} `true` if the request is valid, else `false`
 | 
			
		||||
   * @public
 | 
			
		||||
   */
 | 
			
		||||
  shouldHandle(req) {
 | 
			
		||||
    if (this.options.path) {
 | 
			
		||||
      const index = req.url.indexOf('?');
 | 
			
		||||
      const pathname = index !== -1 ? req.url.slice(0, index) : req.url;
 | 
			
		||||
 | 
			
		||||
      if (pathname !== this.options.path) return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Handle a HTTP Upgrade request.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {http.IncomingMessage} req The request object
 | 
			
		||||
   * @param {(net.Socket|tls.Socket)} socket The network socket between the
 | 
			
		||||
   *     server and client
 | 
			
		||||
   * @param {Buffer} head The first packet of the upgraded stream
 | 
			
		||||
   * @param {Function} cb Callback
 | 
			
		||||
   * @public
 | 
			
		||||
   */
 | 
			
		||||
  handleUpgrade(req, socket, head, cb) {
 | 
			
		||||
    socket.on('error', socketOnError);
 | 
			
		||||
 | 
			
		||||
    const key = req.headers['sec-websocket-key'];
 | 
			
		||||
    const version = +req.headers['sec-websocket-version'];
 | 
			
		||||
 | 
			
		||||
    if (req.method !== 'GET') {
 | 
			
		||||
      const message = 'Invalid HTTP method';
 | 
			
		||||
      abortHandshakeOrEmitwsClientError(this, req, socket, 405, message);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (req.headers.upgrade.toLowerCase() !== 'websocket') {
 | 
			
		||||
      const message = 'Invalid Upgrade header';
 | 
			
		||||
      abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!key || !keyRegex.test(key)) {
 | 
			
		||||
      const message = 'Missing or invalid Sec-WebSocket-Key header';
 | 
			
		||||
      abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (version !== 8 && version !== 13) {
 | 
			
		||||
      const message = 'Missing or invalid Sec-WebSocket-Version header';
 | 
			
		||||
      abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!this.shouldHandle(req)) {
 | 
			
		||||
      abortHandshake(socket, 400);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const secWebSocketProtocol = req.headers['sec-websocket-protocol'];
 | 
			
		||||
    let protocols = new Set();
 | 
			
		||||
 | 
			
		||||
    if (secWebSocketProtocol !== undefined) {
 | 
			
		||||
      try {
 | 
			
		||||
        protocols = subprotocol.parse(secWebSocketProtocol);
 | 
			
		||||
      } catch (err) {
 | 
			
		||||
        const message = 'Invalid Sec-WebSocket-Protocol header';
 | 
			
		||||
        abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const secWebSocketExtensions = req.headers['sec-websocket-extensions'];
 | 
			
		||||
    const extensions = {};
 | 
			
		||||
 | 
			
		||||
    if (
 | 
			
		||||
      this.options.perMessageDeflate &&
 | 
			
		||||
      secWebSocketExtensions !== undefined
 | 
			
		||||
    ) {
 | 
			
		||||
      const perMessageDeflate = new PerMessageDeflate(
 | 
			
		||||
        this.options.perMessageDeflate,
 | 
			
		||||
        true,
 | 
			
		||||
        this.options.maxPayload
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        const offers = extension.parse(secWebSocketExtensions);
 | 
			
		||||
 | 
			
		||||
        if (offers[PerMessageDeflate.extensionName]) {
 | 
			
		||||
          perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]);
 | 
			
		||||
          extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
 | 
			
		||||
        }
 | 
			
		||||
      } catch (err) {
 | 
			
		||||
        const message =
 | 
			
		||||
          'Invalid or unacceptable Sec-WebSocket-Extensions header';
 | 
			
		||||
        abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // Optionally call external client verification handler.
 | 
			
		||||
    //
 | 
			
		||||
    if (this.options.verifyClient) {
 | 
			
		||||
      const info = {
 | 
			
		||||
        origin:
 | 
			
		||||
          req.headers[`${version === 8 ? 'sec-websocket-origin' : 'origin'}`],
 | 
			
		||||
        secure: !!(req.socket.authorized || req.socket.encrypted),
 | 
			
		||||
        req
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      if (this.options.verifyClient.length === 2) {
 | 
			
		||||
        this.options.verifyClient(info, (verified, code, message, headers) => {
 | 
			
		||||
          if (!verified) {
 | 
			
		||||
            return abortHandshake(socket, code || 401, message, headers);
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          this.completeUpgrade(
 | 
			
		||||
            extensions,
 | 
			
		||||
            key,
 | 
			
		||||
            protocols,
 | 
			
		||||
            req,
 | 
			
		||||
            socket,
 | 
			
		||||
            head,
 | 
			
		||||
            cb
 | 
			
		||||
          );
 | 
			
		||||
        });
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!this.options.verifyClient(info)) return abortHandshake(socket, 401);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.completeUpgrade(extensions, key, protocols, req, socket, head, cb);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Upgrade the connection to WebSocket.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {Object} extensions The accepted extensions
 | 
			
		||||
   * @param {String} key The value of the `Sec-WebSocket-Key` header
 | 
			
		||||
   * @param {Set} protocols The subprotocols
 | 
			
		||||
   * @param {http.IncomingMessage} req The request object
 | 
			
		||||
   * @param {(net.Socket|tls.Socket)} socket The network socket between the
 | 
			
		||||
   *     server and client
 | 
			
		||||
   * @param {Buffer} head The first packet of the upgraded stream
 | 
			
		||||
   * @param {Function} cb Callback
 | 
			
		||||
   * @throws {Error} If called more than once with the same socket
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  completeUpgrade(extensions, key, protocols, req, socket, head, cb) {
 | 
			
		||||
    //
 | 
			
		||||
    // Destroy the socket if the client has already sent a FIN packet.
 | 
			
		||||
    //
 | 
			
		||||
    if (!socket.readable || !socket.writable) return socket.destroy();
 | 
			
		||||
 | 
			
		||||
    if (socket[kWebSocket]) {
 | 
			
		||||
      throw new Error(
 | 
			
		||||
        'server.handleUpgrade() was called more than once with the same ' +
 | 
			
		||||
          'socket, possibly due to a misconfiguration'
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this._state > RUNNING) return abortHandshake(socket, 503);
 | 
			
		||||
 | 
			
		||||
    const digest = createHash('sha1')
 | 
			
		||||
      .update(key + GUID)
 | 
			
		||||
      .digest('base64');
 | 
			
		||||
 | 
			
		||||
    const headers = [
 | 
			
		||||
      'HTTP/1.1 101 Switching Protocols',
 | 
			
		||||
      'Upgrade: websocket',
 | 
			
		||||
      'Connection: Upgrade',
 | 
			
		||||
      `Sec-WebSocket-Accept: ${digest}`
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    const ws = new this.options.WebSocket(null);
 | 
			
		||||
 | 
			
		||||
    if (protocols.size) {
 | 
			
		||||
      //
 | 
			
		||||
      // Optionally call external protocol selection handler.
 | 
			
		||||
      //
 | 
			
		||||
      const protocol = this.options.handleProtocols
 | 
			
		||||
        ? this.options.handleProtocols(protocols, req)
 | 
			
		||||
        : protocols.values().next().value;
 | 
			
		||||
 | 
			
		||||
      if (protocol) {
 | 
			
		||||
        headers.push(`Sec-WebSocket-Protocol: ${protocol}`);
 | 
			
		||||
        ws._protocol = protocol;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (extensions[PerMessageDeflate.extensionName]) {
 | 
			
		||||
      const params = extensions[PerMessageDeflate.extensionName].params;
 | 
			
		||||
      const value = extension.format({
 | 
			
		||||
        [PerMessageDeflate.extensionName]: [params]
 | 
			
		||||
      });
 | 
			
		||||
      headers.push(`Sec-WebSocket-Extensions: ${value}`);
 | 
			
		||||
      ws._extensions = extensions;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // Allow external modification/inspection of handshake headers.
 | 
			
		||||
    //
 | 
			
		||||
    this.emit('headers', headers, req);
 | 
			
		||||
 | 
			
		||||
    socket.write(headers.concat('\r\n').join('\r\n'));
 | 
			
		||||
    socket.removeListener('error', socketOnError);
 | 
			
		||||
 | 
			
		||||
    ws.setSocket(socket, head, {
 | 
			
		||||
      maxPayload: this.options.maxPayload,
 | 
			
		||||
      skipUTF8Validation: this.options.skipUTF8Validation
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (this.clients) {
 | 
			
		||||
      this.clients.add(ws);
 | 
			
		||||
      ws.on('close', () => {
 | 
			
		||||
        this.clients.delete(ws);
 | 
			
		||||
 | 
			
		||||
        if (this._shouldEmitClose && !this.clients.size) {
 | 
			
		||||
          process.nextTick(emitClose, this);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cb(ws, req);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = WebSocketServer;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Add event listeners on an `EventEmitter` using a map of <event, listener>
 | 
			
		||||
 * pairs.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {EventEmitter} server The event emitter
 | 
			
		||||
 * @param {Object.<String, Function>} map The listeners to add
 | 
			
		||||
 * @return {Function} A function that will remove the added listeners when
 | 
			
		||||
 *     called
 | 
			
		||||
 * @private
 | 
			
		||||
 */
 | 
			
		||||
function addListeners(server, map) {
 | 
			
		||||
  for (const event of Object.keys(map)) server.on(event, map[event]);
 | 
			
		||||
 | 
			
		||||
  return function removeListeners() {
 | 
			
		||||
    for (const event of Object.keys(map)) {
 | 
			
		||||
      server.removeListener(event, map[event]);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Emit a `'close'` event on an `EventEmitter`.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {EventEmitter} server The event emitter
 | 
			
		||||
 * @private
 | 
			
		||||
 */
 | 
			
		||||
function emitClose(server) {
 | 
			
		||||
  server._state = CLOSED;
 | 
			
		||||
  server.emit('close');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handle socket errors.
 | 
			
		||||
 *
 | 
			
		||||
 * @private
 | 
			
		||||
 */
 | 
			
		||||
function socketOnError() {
 | 
			
		||||
  this.destroy();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Close the connection when preconditions are not fulfilled.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {(net.Socket|tls.Socket)} socket The socket of the upgrade request
 | 
			
		||||
 * @param {Number} code The HTTP response status code
 | 
			
		||||
 * @param {String} [message] The HTTP response body
 | 
			
		||||
 * @param {Object} [headers] Additional HTTP response headers
 | 
			
		||||
 * @private
 | 
			
		||||
 */
 | 
			
		||||
function abortHandshake(socket, code, message, headers) {
 | 
			
		||||
  //
 | 
			
		||||
  // The socket is writable unless the user destroyed or ended it before calling
 | 
			
		||||
  // `server.handleUpgrade()` or in the `verifyClient` function, which is a user
 | 
			
		||||
  // error. Handling this does not make much sense as the worst that can happen
 | 
			
		||||
  // is that some of the data written by the user might be discarded due to the
 | 
			
		||||
  // call to `socket.end()` below, which triggers an `'error'` event that in
 | 
			
		||||
  // turn causes the socket to be destroyed.
 | 
			
		||||
  //
 | 
			
		||||
  message = message || http.STATUS_CODES[code];
 | 
			
		||||
  headers = {
 | 
			
		||||
    Connection: 'close',
 | 
			
		||||
    'Content-Type': 'text/html',
 | 
			
		||||
    'Content-Length': Buffer.byteLength(message),
 | 
			
		||||
    ...headers
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  socket.once('finish', socket.destroy);
 | 
			
		||||
 | 
			
		||||
  socket.end(
 | 
			
		||||
    `HTTP/1.1 ${code} ${http.STATUS_CODES[code]}\r\n` +
 | 
			
		||||
      Object.keys(headers)
 | 
			
		||||
        .map((h) => `${h}: ${headers[h]}`)
 | 
			
		||||
        .join('\r\n') +
 | 
			
		||||
      '\r\n\r\n' +
 | 
			
		||||
      message
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Emit a `'wsClientError'` event on a `WebSocketServer` if there is at least
 | 
			
		||||
 * one listener for it, otherwise call `abortHandshake()`.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {WebSocketServer} server The WebSocket server
 | 
			
		||||
 * @param {http.IncomingMessage} req The request object
 | 
			
		||||
 * @param {(net.Socket|tls.Socket)} socket The socket of the upgrade request
 | 
			
		||||
 * @param {Number} code The HTTP response status code
 | 
			
		||||
 * @param {String} message The HTTP response body
 | 
			
		||||
 * @private
 | 
			
		||||
 */
 | 
			
		||||
function abortHandshakeOrEmitwsClientError(server, req, socket, code, message) {
 | 
			
		||||
  if (server.listenerCount('wsClientError')) {
 | 
			
		||||
    const err = new Error(message);
 | 
			
		||||
    Error.captureStackTrace(err, abortHandshakeOrEmitwsClientError);
 | 
			
		||||
 | 
			
		||||
    server.emit('wsClientError', err, socket, req);
 | 
			
		||||
  } else {
 | 
			
		||||
    abortHandshake(socket, code, message);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1311
									
								
								node_modules/ws/lib/websocket.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1311
									
								
								node_modules/ws/lib/websocket.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user