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 || undefined;
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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