websocket13
Version:
Simple WebSocket protocol 13 client with no native or heavy dependencies
743 lines • 65.8 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const bytebuffer_1 = __importDefault(require("bytebuffer"));
const crypto_1 = require("crypto");
const permessage_deflate_1 = __importDefault(require("permessage-deflate"));
const tiny_typed_emitter_1 = require("tiny-typed-emitter");
const websocket_extensions_1 = __importDefault(require("websocket-extensions"));
const StreamedIncomingMessage_1 = __importDefault(require("./streams/StreamedIncomingMessage"));
const StreamedOutgoingMessage_1 = __importDefault(require("./streams/StreamedOutgoingMessage"));
const State_1 = __importDefault(require("./enums/State"));
const tls_1 = require("tls");
const StatusCode_1 = __importDefault(require("./enums/StatusCode"));
const FrameType_1 = __importDefault(require("./enums/FrameType"));
class WebSocketBase extends tiny_typed_emitter_1.TypedEmitter {
constructor() {
super();
this.state = State_1.default.Closed;
this._extensions = new websocket_extensions_1.default();
this._extensions.add(permessage_deflate_1.default);
this.protocol = null;
this.stats = { tx: { wire: 0, preExt: 0 }, rx: { wire: 0, postExt: 0 } };
this.options = {
pingInterval: 10000,
pingTimeout: 10000,
pingFailures: 3
};
this._data = {};
this._outgoingFrames = []; // holds frame objects which we haven't sent yet
this._dataBuffer = Buffer.alloc(0); // holds raw TCP data that we haven't processed yet
this._incomingStream = null; // StreamedIncomingMessage object for the current message
this._extensionProcessingOutgoingFrameId = 0;
}
_onConnected() {
this._pingFailures = 0;
this._queuePing();
}
/**
* Disconnect the websocket gracefully.
* @param {number} [code=StatusCode.NormalClosure] - A value from the StatusCode enum to send to the other side
* @param {string} [reason] - An optional reason string to send to the other side
*/
disconnect(code, reason) {
if (this.state == State_1.default.Connecting && this._socket) {
this._socket.end();
// @ts-ignore
this._socket.destroy();
this.state = State_1.default.Closed;
}
else if (this.state == State_1.default.Connecting && !this._socket) {
this.state = State_1.default.Closing;
}
else if (this.state == State_1.default.Connected) {
code = code || StatusCode_1.default.NormalClosure;
reason = reason || '';
let buf = new bytebuffer_1.default(2 + reason.length, bytebuffer_1.default.BIG_ENDIAN);
buf.writeUint16(code);
buf.writeString(reason);
this._sendControl(FrameType_1.default.Control.Close, buf.flip().toBuffer());
this._outgoingFrames = []; // empty the queue; we can't send any more data now
this.state = State_1.default.Closing;
setTimeout(() => {
if (this.state != State_1.default.Closed) {
this._closeExtensions(() => {
this._socket.end();
});
}
}, 5000).unref();
}
else {
throw new Error('Cannot disconnect a WebSocket that is not connected.');
}
}
/**
* Send some data in a single frame (not streamed).
* @param {string|Buffer} data - The data to send. If a string, the data will be sent as UTF-8 text. If a Buffer, it will be sent as binary data.
*/
send(data) {
let opcode = (typeof data === 'string' ? FrameType_1.default.Data.Text : FrameType_1.default.Data.Binary);
if (bytebuffer_1.default.isByteBuffer(data)) {
data = data.toBuffer();
}
else if (typeof data === 'string') {
data = Buffer.from(data, 'utf8');
}
this._sendFrame({
FIN: true,
RSV1: false,
RSV2: false,
RSV3: false,
opcode,
payloadLength: data.length,
payload: data
});
}
createMessageStream(type) {
let frame = new StreamedOutgoingMessage_1.default(this, type);
this._outgoingFrames.push(frame);
return frame;
}
data(key, value) {
let val = this._data[key];
if (typeof value === 'undefined') {
return val;
}
this._data[key] = value;
return val;
}
getPeerCertificate(detailed) {
if (!(this._socket instanceof tls_1.TLSSocket)) {
return null;
}
let socket = this._socket;
return socket.getPeerCertificate(detailed);
}
getSecurityProtocol() {
if (!(this._socket instanceof tls_1.TLSSocket)) {
return null;
}
let socket = this._socket;
// @ts-ignore
return socket.getProtocol();
}
_prepSocketEvents() {
this.remoteAddress = this._socket.remoteAddress;
this._socket.on('data', (data) => {
if ([State_1.default.Connected, State_1.default.Closing, State_1.default.ClosingError].includes(this.state)) {
this._handleData(data);
}
});
this._socket.on('close', () => {
this._cleanupTimers();
if (this.state == State_1.default.ClosingError) {
this.state = State_1.default.Closed;
return;
}
if (this.state == State_1.default.Closed) {
this.emit('debug', 'Socket closed after successful websocket closure.');
return;
}
let state = this.state;
this.state = State_1.default.Closed;
this.emit('disconnected', StatusCode_1.default.AbnormalTermination, 'Socket closed', state == State_1.default.Closing);
this.emit('disconnect', StatusCode_1.default.AbnormalTermination, 'Socket closed', state == State_1.default.Closing); // save people from typos
this._closeExtensions();
this._cleanupTimers();
});
this._socket.on('error', (err) => {
if (this.state == State_1.default.Closed || this.state == State_1.default.ClosingError) {
// Ignore errors that come after the socket is closed (e.g. ECONNRESET when we respond to Close frames)
return;
}
err.state = this.state;
this.state = State_1.default.ClosingError;
this._closeExtensions();
this._cleanupTimers();
this.emit('error', err);
});
}
setTimeout(timeout, callback) {
if (this._userTimeout) {
clearTimeout(this._userTimeout);
}
delete this._userTimeout;
delete this._userTimeoutMs;
if (timeout == 0) {
return this;
}
this._userTimeoutMs = timeout;
this._resetUserTimeout();
if (typeof callback === 'function') {
this.once('timeout', callback);
}
}
_resetUserTimeout() {
if (this._userTimeout) {
clearTimeout(this._userTimeout);
delete this._userTimeout;
}
if (this._userTimeoutMs) {
this._userTimeout = setTimeout(() => {
delete this._userTimeout;
this.setTimeout(0); // don't keep triggering timeout
this.emit('timeout');
}, this._userTimeoutMs);
}
}
sendPing(callback) {
this._pingCallbacks = this._pingCallbacks || {};
let pingData, pingNum;
do {
pingData = (0, crypto_1.randomBytes)(4);
pingNum = pingData.readUInt32BE(0);
} while (this._pingCallbacks[pingNum]);
// eslint-disable-next-line
this._pingCallbacks[pingNum] = callback || function () { };
this._sendFrame({
FIN: true,
RSV1: false,
RSV2: false,
RSV3: false,
opcode: FrameType_1.default.Control.Ping,
payloadLength: pingData.length,
payload: pingData
}, true);
}
_queuePing() {
clearTimeout(this._pingTimer);
clearTimeout(this._pingTimeout);
if (this.state != State_1.default.Connected || !this.options.pingInterval || !this.options.pingTimeout || !this.options.pingFailures) {
return;
}
this._pingTimer = setTimeout(() => {
if (this.state != State_1.default.Connected) {
return;
}
let time = Date.now();
this.sendPing(() => {
this.emit('latency', Date.now() - time);
this._pingFailures = 0;
this._queuePing();
});
this._pingTimeout = setTimeout(() => {
if (this.state != State_1.default.Connected) {
return;
}
this.emit('debug', `Ping timeout #${this._pingFailures + 1}`);
if (++this._pingFailures >= this.options.pingFailures) {
this._terminateError(StatusCode_1.default.PolicyViolation, 'Ping timeout');
}
else {
this._queuePing();
}
}, this.options.pingTimeout);
}, this.options.pingInterval);
}
_handleData(data) {
if (data && data.length > 0) {
this._dataBuffer = Buffer.concat([this._dataBuffer, data]);
this._queuePing(); // reset the ping timer
}
if (this._dataBuffer.length == 0) {
return;
}
let buf = bytebuffer_1.default.wrap(this._dataBuffer, bytebuffer_1.default.BIG_ENDIAN);
let frame = null;
try {
let byte = buf.readUint8();
let fin = !!(byte & (1 << 7));
let rsv1 = !!(byte & (1 << 6));
let rsv2 = !!(byte & (1 << 5));
let rsv3 = !!(byte & (1 << 4));
let opcode = byte & 0x0F;
byte = buf.readUint8();
let hasMask = !!(byte & (1 << 7));
let payloadLength = byte & 0x7F;
if (payloadLength == 126) {
payloadLength = buf.readUint16();
}
else if (payloadLength == 127) {
payloadLength = parseInt(buf.readUint64(), 10);
}
let maskKey = null;
if (hasMask) {
maskKey = buf.readUint32();
}
if (buf.remaining() < payloadLength) {
return; // We don't have the entire payload yet
}
let payload = buf.slice(buf.offset, buf.offset + payloadLength).toBuffer();
buf.skip(payloadLength);
// got the full frame
frame = {
FIN: fin,
RSV1: rsv1,
RSV2: rsv2,
RSV3: rsv3,
opcode,
payloadLength,
maskKey,
payload
};
}
catch (ex) {
// We don't have the full data yet. No worries.
return;
}
// We have a full frame
this._dataBuffer = buf.toBuffer();
this._handleFrame(frame);
this._handleData();
}
_handleFrame(frame) {
// Flags: FIN, RSV1, RSV2, RSV3
// Ints: opcode (4 bits), payloadLength (up to 64 bits), maskKey (32 bits)
// Binary: payload
let overheadLength = getFrameOverheadLength(frame);
this.stats.rx.wire += overheadLength + frame.payload.length;
this.stats.rx.postExt += overheadLength; // extensions can't change overhead length
let debugMsg = `Got frame ${frame.opcode.toString(16).toUpperCase()}, ${frame.FIN ? 'FIN, ' : ''}`;
for (let i = 1; i <= 3; i++) {
if (frame['RSV' + i]) {
debugMsg += `RSV${i}, `;
}
}
debugMsg += (frame.maskKey ? 'MASK, ' : '') + `payload ${frame.payload.length} bytes`;
this.emit('debug', debugMsg);
if (this.state != State_1.default.Connected &&
!((this.state == State_1.default.ClosingError || this.state == State_1.default.Closing) &&
frame.opcode == FrameType_1.default.Control.Close)) {
this.emit('debug', `Got frame ${frame.opcode.toString(16)} while in state ${this.state}`);
return;
}
// The RFC requires us to terminate the connection if we get an unmasked frame from a client or a masked frame from
// a server. But in the real world, implementations are bad sometimes so for compatibility's sake, just log it.
if ((this._type == 'server' && !frame.maskKey && frame.payload.length > 0) ||
(this._type == 'client' && frame.maskKey)) {
this.emit('debug', `Protocol violation: Received ${frame.maskKey ? 'masked' : 'unmasked'} frame ` +
`${frame.opcode.toString(16).toUpperCase()} of length ${frame.payload.length} from ${this._type == 'client' ? 'server' : 'client'}`);
}
// Unmask if applicable
if (frame.maskKey !== null && frame.payload && frame.payload.length > 0) {
frame.payload = maskOrUnmask(frame.payload, frame.maskKey);
}
// Check to make sure RSV bits are valid
if (this._extensions && !this._extensions.validFrameRsv(getExtensionFrame(frame))) {
this._terminateError(StatusCode_1.default.ProtocolError, 'Unexpected reserved bit set');
return;
}
let payload;
// Is this a control frame? They need to be handled before anything else as they can be interjected between
// fragmented message frames.
if (frame.opcode & (1 << 3)) {
// this is a control frame.
if (!frame.FIN) {
this._terminateError(StatusCode_1.default.ProtocolError, `Got a fragmented control frame ${frame.opcode.toString(16)}`);
return;
}
if (frame.payload.length > 125) {
this._terminateError(StatusCode_1.default.ProtocolError, `Got a control frame ${frame.opcode.toString(16)} with invalid payload length ${frame.payload.length}`);
return;
}
// Run it through extensions
this._extensions.processIncomingMessage(getExtensionMessage(frame), (err, msg) => {
if (err) {
this._terminateError(StatusCode_1.default.ProtocolError, err.message || err);
return;
}
frame = fromExtensionMessage(msg);
this.stats.rx.postExt += frame.payload.length;
switch (frame.opcode) {
case FrameType_1.default.Control.Close:
let code = StatusCode_1.default.NoStatusCode;
let reason = '';
if (frame.payload && frame.payload.length >= 2) {
code = frame.payload.readUInt16BE(0);
if (frame.payload.length > 2) {
reason = frame.payload.toString('utf8', 2);
}
}
let state = this.state;
if (state == State_1.default.Closing || state == State_1.default.ClosingError) {
this._cleanupTimers();
this._closeExtensions(() => {
this._socket.end();
});
// We're all done here
}
else {
if (code != StatusCode_1.default.NoStatusCode) {
payload = new bytebuffer_1.default(2 + reason.length, bytebuffer_1.default.BIG_ENDIAN);
payload.writeUint16(code);
payload.writeString(reason || '');
}
else {
payload = new bytebuffer_1.default(0, bytebuffer_1.default.BIG_ENDIAN); // don't send anything back
}
this._sendControl(FrameType_1.default.Control.Close, payload.flip().toBuffer());
this._cleanupTimers();
this._closeExtensions(() => {
this._socket.end();
});
}
this.state = State_1.default.Closed;
if (state != State_1.default.ClosingError) {
this.emit('disconnected', code, reason, state == State_1.default.Closing);
this.emit('disconnect', code, reason, state == State_1.default.Closing); // save people from typos
}
break;
case FrameType_1.default.Control.Ping:
this._sendControl(FrameType_1.default.Control.Pong, frame.payload);
break;
case FrameType_1.default.Control.Pong:
if (frame.payload && frame.payload.length == 4) {
let num = frame.payload.readUInt32BE(0);
if (this._pingCallbacks[num]) {
this._pingCallbacks[num]();
delete this._pingCallbacks[num];
}
}
break;
default:
this._terminateError(StatusCode_1.default.UnacceptableDataType, `Unknown control frame type ${frame.opcode.toString(16).toUpperCase()}`);
}
});
return;
}
// Sanity checks
if (!this._incomingStream && frame.opcode == FrameType_1.default.Continuation) {
this._terminateError(StatusCode_1.default.ProtocolError, 'Received continuation frame without initial frame.');
return;
}
else if (this._incomingStream && frame.opcode != FrameType_1.default.Continuation) {
this._terminateError(StatusCode_1.default.ProtocolError, 'Received new message without finishing a fragmented one.');
return;
}
// this is not a control frame.
this._resetUserTimeout();
// Is this the first frame of a fragmented message?
if (!frame.FIN && !this._incomingStream) {
this.emit('debug', 'Got first frame of fragmented message.');
let dispatch = this.listenerCount('streamedMessage') >= 1 && !frame.RSV1 && !frame.RSV2 && !frame.RSV3;
this._incomingStream = new StreamedIncomingMessage_1.default(frame, dispatch);
if (dispatch) {
this.emit('streamedMessage', frame.opcode, this._incomingStream);
}
this._incomingStream.on('end', data => {
if (!dispatch) {
let frame = this._incomingStream.frameHeader;
frame.payload = data;
frame.payloadLength = frame.payload.length;
this._dispatchDataFrame(frame);
}
});
// record this start frame in stats only if we've dispatched the stream. if we haven't, we'll process the whole
// message as one.
if (dispatch) {
this.stats.rx.postExt += frame.payload.length;
}
return;
}
if (frame.opcode == FrameType_1.default.Continuation) {
this.emit('debug', 'Got continuation frame');
this._incomingStream._frame(frame);
// record this frame in stats only if we've dispatched the stream. if we haven't, we'll process the whole
// message as one.
if (this._incomingStream._dispatched) {
this.stats.rx.postExt += frame.payload.length;
}
if (frame.FIN) {
this._incomingStream = null;
}
return;
}
// We know that we have this entire frame now. Let's handle it.
this._dispatchDataFrame(frame);
}
_dispatchDataFrame(frame) {
this._extensions.processIncomingMessage(getExtensionMessage(frame), (err, msg) => {
if (err) {
this._terminateError(StatusCode_1.default.ProtocolError, err.message || err);
return;
}
frame = fromExtensionMessage(msg);
this.stats.rx.postExt += frame.payload.length;
switch (frame.opcode) {
case FrameType_1.default.Data.Text:
let utf8 = frame.payload.toString('utf8');
// Check that the UTF-8 is valid
if (Buffer.compare(Buffer.from(utf8, 'utf8'), frame.payload) !== 0) {
// This is invalid. We must tear down the connection.
this._terminateError(StatusCode_1.default.InconsistentData, 'Received invalid UTF-8 data in a text frame.');
return;
}
this.emit('message', FrameType_1.default.Data.Text, utf8);
break;
case FrameType_1.default.Data.Binary:
this.emit('message', FrameType_1.default.Data.Binary, frame.payload);
break;
default:
this._terminateError(StatusCode_1.default.UnacceptableDataType, `Unknown data frame type ${frame.opcode.toString(16).toUpperCase()}`);
}
});
}
_sendFrame(frame, bypassQueue = false) {
// eslint-disable-next-line
let self = this;
let isControl = !!(frame.opcode & (1 << 3));
if (this.state != State_1.default.Connected && !(this.state == State_1.default.Closing && isControl)) {
throw new Error(`Cannot send data while not connected (state ${this.state})`);
}
if (typeof frame.FIN === 'undefined') {
frame.FIN = true;
}
if (isControl) {
if (frame.payload && frame.payload.length > 125) {
throw new Error(`Cannot send control frame ${frame.opcode.toString(16).toUpperCase()} with ${frame.payload.length} bytes of payload data. Payload must be 125 bytes or fewer.`);
}
bypassQueue = true; // we can send control messages whenever
}
frame.payload = frame.payload || Buffer.alloc(0);
let maskKey = frame.maskKey;
let fin = frame.FIN;
let queueId = null;
// Calculate how long this frame would be as it stands now
// All frames are at least 2 bytes; byte 1 is FIN, RSV1-3, opcode; byte 2 is MASK bit, payload length
let preExtLength = 2 + frame.payload.length;
if (frame.maskKey) {
preExtLength += 4; // mask keys are always 4 bytes
}
preExtLength += getExtraPayloadLengthFieldSize(frame.payload.length);
this.stats.tx.preExt += preExtLength;
if (isControl || !frame.FIN || frame.opcode == 0) {
// https://github.com/faye/permessage-deflate-node/issues/6
onExtensionsProcessed(frame);
}
else {
if (!bypassQueue) {
queueId = ++this._extensionProcessingOutgoingFrameId;
this._outgoingFrames.push(queueId);
// What is queueId? It's a placeholder. We want to retain the order guarantee, but we still need to pass this message
// to extensions. Those might not call back in order. Consequently, we "reserve the message's place" in the outgoing
// queue with a number. That array position will be replaced with the actual message when it's ready.
if (queueId >= 4294967295) {
// just for fun. this is unlikely to ever really happen. 4294967295 is max uint32 and is totally arbitrary, we can go up to 2^53
this._extensionProcessingOutgoingFrameId = 0;
}
}
this._extensions.processOutgoingMessage(getExtensionMessage(frame), (err, msg) => {
if (err) {
this._terminateError(StatusCode_1.default.ProtocolError, err.message || err);
return;
}
frame = fromExtensionMessage(msg);
frame.maskKey = maskKey;
frame.FIN = fin;
onExtensionsProcessed(frame);
});
}
function onExtensionsProcessed(frame) {
let debugMsg = `${bypassQueue ? 'Sending' : 'Queueing'} frame ${frame.opcode.toString(16).toUpperCase()}, ${frame.FIN ? 'FIN, ' : ''}`;
for (let i = 1; i <= 3; i++) {
if (frame['RSV' + i]) {
debugMsg += `RSV${i}, `;
}
}
debugMsg += (frame.maskKey ? 'MASK, ' : '') + `payload ${frame.payload.length} bytes`;
self.emit('debug', debugMsg);
let size = 0;
size += 1; // FIN, RSV1, RSV2, RSV3, opcode
size += 1; // MASK, payload length
size += getExtraPayloadLengthFieldSize(frame.payload.length);
if (frame.maskKey) {
size += 4;
}
size += frame.payload.length;
let buf = new bytebuffer_1.default(size, bytebuffer_1.default.BIG_ENDIAN);
let byte = 0;
byte |= (frame.FIN ? 1 : 0) << 7;
byte |= (frame.RSV1 ? 1 : 0) << 6;
byte |= (frame.RSV2 ? 1 : 0) << 5;
byte |= (frame.RSV3 ? 1 : 0) << 4;
byte |= frame.opcode & 0x0F;
buf.writeUint8(byte);
byte = 0;
byte |= (frame.maskKey ? 1 : 0) << 7;
if (frame.payload.length <= 125) {
byte |= frame.payload.length;
buf.writeUint8(byte);
}
else if (frame.payload.length <= 65535) {
byte |= 126;
buf.writeUint8(byte);
buf.writeUint16(frame.payload.length);
}
else {
byte |= 127;
buf.writeUint8(byte);
buf.writeUint64(frame.payload.length);
}
if (frame.maskKey) {
buf.writeUint32(frame.maskKey);
buf.append(maskOrUnmask(frame.payload, frame.maskKey));
}
else {
buf.append(frame.payload);
}
// we're done building the buffer, so go ahead and convert it to a node Buffer
buf = buf.flip().toBuffer();
self.stats.tx.wire += buf.length;
if (bypassQueue) {
self._socket.write(buf);
}
else if (queueId) {
// This already has a placeholder in the queue
let idx = self._outgoingFrames.indexOf(queueId);
if (idx == -1) {
self._outgoingFrames.push(buf);
}
else {
self._outgoingFrames[idx] = buf;
}
}
else {
// No queue placeholder, just stick it in
self._outgoingFrames.push(buf);
}
self._processQueue();
}
}
_processQueue() {
let frames = this._outgoingFrames.slice(0);
while (frames.length > 0) {
if (typeof frames[0] === 'number') {
// This is a placeholder, so we're done
break;
}
if (frames[0] instanceof StreamedOutgoingMessage_1.default) {
if (!frames[0].started) {
this.emit('debug', 'Starting StreamedOutgoingMessage');
frames[0]._start();
}
if (frames[0].finished) {
frames.splice(0, 1);
continue;
}
break;
}
this._socket.write(frames.splice(0, 1)[0]);
}
this._outgoingFrames = frames;
}
_sendControl(opcode, payload) {
if (this.state == State_1.default.Closed || !this._socket) {
return;
}
this._sendFrame({
opcode,
payload,
payloadLength: payload.length,
FIN: true,
RSV1: false,
RSV2: false,
RSV3: false
});
}
_closeError(err) {
err.state = this.state;
this.state = State_1.default.Closed;
this._closeExtensions();
this._cleanupTimers();
if (this._socket) {
this._socket.end();
// @ts-ignore
this._socket.destroy();
}
this.emit('error', err);
}
_terminateError(code, message) {
let err = new Error(message);
err.state = this.state;
err.code = code;
this.disconnect(code, message);
this.state = State_1.default.ClosingError;
this.emit('error', err);
}
_cleanupTimers() {
clearTimeout(this._pingTimeout);
clearTimeout(this._pingTimer);
clearTimeout(this._userTimeout);
}
_closeExtensions(callback) {
// eslint-disable-next-line
callback = callback || function () { };
try {
this._extensions.close(callback);
}
catch (ex) {
callback();
}
}
}
exports.default = WebSocketBase;
// Util
function maskOrUnmask(data, maskKey) {
let key = Buffer.alloc(4);
key.writeUInt32BE(maskKey, 0);
for (let i = 0; i < data.length; i++) {
data[i] ^= key[i % 4];
}
return data;
}
function getExtensionFrame(frame) {
return {
final: frame.FIN,
rsv1: frame.RSV1,
rsv2: frame.RSV2,
rsv3: frame.RSV3,
opcode: frame.opcode,
masked: !!frame.maskKey,
maskingKey: frame.maskKey,
payload: frame.payload
};
}
function getExtensionMessage(frame) {
return {
rsv1: frame.RSV1,
rsv2: frame.RSV2,
rsv3: frame.RSV3,
opcode: frame.opcode,
data: frame.payload
};
}
function fromExtensionMessage(msg) {
return {
FIN: true,
RSV1: msg.rsv1,
RSV2: msg.rsv2,
RSV3: msg.rsv3,
opcode: msg.opcode,
payloadLength: msg.data.length,
payload: msg.data
};
}
function getFrameOverheadLength(frame) {
return 2 // byte 1 = FIN, RSV1-3, opcode; byte 2 = MASK flag, payload length
+ (frame.maskKey ? 4 : 0) // mask keys are always 4 bytes if present
+ getExtraPayloadLengthFieldSize(frame.payload.length);
}
function getExtraPayloadLengthFieldSize(payloadLength) {
if (payloadLength >= 126 && payloadLength <= 65535) {
return 2; // 16-bit payload length
}
else if (payloadLength > 65535) {
return 8; // 64-bit payload length
}
else {
return 0; // no extra payload length field
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiV2ViU29ja2V0QmFzZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9XZWJTb2NrZXRCYXNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEsNERBQW9DO0FBQ3BDLG1DQUFtQztBQUNuQyw0RUFBbUQ7QUFDbkQsMkRBQWdEO0FBQ2hELGdGQUF1RDtBQUV2RCxnR0FBd0U7QUFDeEUsZ0dBQXdFO0FBRXhFLDBEQUFrQztBQVVsQyw2QkFBOEI7QUFDOUIsb0VBQTRDO0FBRTVDLGtFQUEwQztBQUUxQyxNQUFxQixhQUFjLFNBQVEsaUNBQTZCO0lBc0J2RTtRQUNDLEtBQUssRUFBRSxDQUFDO1FBRVIsSUFBSSxDQUFDLEtBQUssR0FBRyxlQUFLLENBQUMsTUFBTSxDQUFDO1FBQzFCLElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSw4QkFBbUIsRUFBRSxDQUFDO1FBQzdDLElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFDLDRCQUFpQixDQUFDLENBQUM7UUFDeEMsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUM7UUFDckIsSUFBSSxDQUFDLEtBQUssR0FBRyxFQUFDLEVBQUUsRUFBRSxFQUFDLElBQUksRUFBRSxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUMsRUFBQyxFQUFFLEVBQUUsRUFBRSxFQUFDLElBQUksRUFBRSxDQUFDLEVBQUUsT0FBTyxFQUFFLENBQUMsRUFBQyxFQUFDLENBQUM7UUFFbkUsSUFBSSxDQUFDLE9BQU8sR0FBRztZQUNkLFlBQVksRUFBRSxLQUFLO1lBQ25CLFdBQVcsRUFBRSxLQUFLO1lBQ2xCLFlBQVksRUFBRSxDQUFDO1NBQ2YsQ0FBQztRQUVGLElBQUksQ0FBQyxLQUFLLEdBQUcsRUFBRSxDQUFDO1FBQ2hCLElBQUksQ0FBQyxlQUFlLEdBQUcsRUFBRSxDQUFDLENBQUMsZ0RBQWdEO1FBQzNFLElBQUksQ0FBQyxXQUFXLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxD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