Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

websocket13

Package Overview
Dependencies
Maintainers
1
Versions
42
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

websocket13 - npm Package Compare versions

Comparing version 1.1.2 to 1.2.0-beta1

4

examples/echoclient.js

@@ -7,3 +7,3 @@ /**

var socket = new WS13.WebSocket('ws://echo.websocket.org', {
var socket = new WS13.WebSocket('ws://127.0.0.1:8080', {
"protocols": ["foo", "bar"] // supported subprotocols

@@ -70,1 +70,3 @@ });

});
socket.on('debug', console.log);
var WS13 = require('./index.js');
var Extensions = require('websocket-extensions');
var StreamedOutgoingMessage = require('./StreamedOutgoingMessage.js');

@@ -13,3 +14,4 @@ var StreamedIncomingMessage = require('./StreamedIncomingMessage.js');

this.state = WS13.State.Closed;
this.extensions = [];
this.extensions = new Extensions();
this.extensions.add(require('permessage-deflate'));
this.protocol = null;

@@ -27,2 +29,3 @@

this._incomingStream = null; // StreamedIncomingMessage object for the current message
this._extensionProcessingOutgoingFrameId = 0;

@@ -53,2 +56,3 @@ this.on('connected', () => {

this._sendControl(WS13.FrameType.Control.Close, buf.flip().toBuffer());
this._outgoingFrames = []; // empty the queue; we can't send any more data now
this.state = WS13.State.Closing;

@@ -58,3 +62,5 @@

if (this.state != WS13.State.Closed) {
this._socket.end();
this._closeExtensions(() => {
this._socket.end();
});
}

@@ -114,3 +120,3 @@ }, 5000);

if (this.state == WS13.State.ClosingError) {
this.state = WS13.State.Closing;
this.state = WS13.State.Closed;
return;

@@ -127,2 +133,3 @@ }

this.emit('disconnected', WS13.StatusCode.AbnormalTermination, "Socket closed", state == WS13.State.Closing);
this._closeExtensions();
});

@@ -133,2 +140,3 @@

this.state = WS13.State.ClosingError;
this._closeExtensions();
this.emit('error', err);

@@ -243,18 +251,14 @@ });

// We don't have any extensions, so all the RSV bits must be 0 or else we have to bail
if (frame.RSV1 || frame.RSV2 || frame.RSV3) {
var bit = (frame.RSV1 ? 'RSV1' : (frame.RSV2 ? 'RSV2' : 'RSV3'));
this._terminateError(WS13.StatusCode.ProtocolError, "Unexpected reserved bit " + bit + "set");
return;
var debugMsg = "Got frame " + frame.opcode.toString(16).toUpperCase() + ", " + (frame.FIN ? "FIN, " : "");
for (var i = 1; i <= 3; i++) {
if (frame['RSV' + i]) {
debugMsg += "RSV" + i + ", ";
}
}
this.emit('debug', "Got frame " + frame.opcode.toString(16).toUpperCase() + ", " + (frame.FIN ? "FIN, " : "") +
(frame.maskKey ? "MASK, " : "") + "payload " + frame.payload.length + " bytes");
debugMsg += (frame.maskKey ? "MASK, " : "") + "payload " + frame.payload.length + " bytes";
if (
this.state != WS13.State.Connected && !(
(this.state == WS13.State.ClosingError || this.state == WS13.State.Closing) &&
frame.opcode == WS13.FrameType.Control.Close
)
) {
this.emit('debug', debugMsg);
if (this.state != WS13.State.Connected && !((this.state == WS13.State.ClosingError || this.state == WS13.State.Closing) && frame.opcode == WS13.FrameType.Control.Close)) {
this.emit('debug', "Got frame " + frame.opcode.toString(16) + " while in state " + this.state);

@@ -269,2 +273,8 @@ return;

// Check to make sure RSV bits are valid
if (this.extensions && !this.extensions.validFrameRsv(getExtensionFrame(frame))) {
this._terminateError(WS13.StatusCode.ProtocolError, "Unexpected reserved bit set");
return;
}
var payload;

@@ -285,54 +295,72 @@

switch (frame.opcode) {
case WS13.FrameType.Control.Close:
var code = WS13.StatusCode.NoStatusCode;
var reason = "";
// Run it through extensions
this.extensions.processIncomingMessage(getExtensionMessage(frame), (err, msg) => {
if (err) {
this._terminateError(WS13.StatusCode.ProtocolError, err.message || err);
return;
}
if (frame.payload && frame.payload.length >= 2) {
code = frame.payload.readUInt16BE(0);
frame = fromExtensionMessage(msg);
if (frame.payload.length > 2) {
reason = frame.payload.toString('utf8', 2);
switch (frame.opcode) {
case WS13.FrameType.Control.Close:
var code = WS13.StatusCode.NoStatusCode;
var reason = "";
if (frame.payload && frame.payload.length >= 2) {
code = frame.payload.readUInt16BE(0);
if (frame.payload.length > 2) {
reason = frame.payload.toString('utf8', 2);
}
}
}
var state = this.state;
var state = this.state;
if (state == WS13.State.Closing || state == WS13.State.ClosingError) {
this._socket.end();
// We're all done here
} else {
if (code != WS13.StatusCode.NoStatusCode) {
payload = new ByteBuffer(2 + reason.length, ByteBuffer.BIG_ENDIAN);
payload.writeUint16(code);
payload.writeString(reason || "");
if (state == WS13.State.Closing || state == WS13.State.ClosingError) {
this._closeExtensions(() => {
this._socket.end();
});
// We're all done here
} else {
payload = new ByteBuffer(0, ByteBuffer.BIG_ENDIAN); // don't send anything back
if (code != WS13.StatusCode.NoStatusCode) {
payload = new ByteBuffer(2 + reason.length, ByteBuffer.BIG_ENDIAN);
payload.writeUint16(code);
payload.writeString(reason || "");
} else {
payload = new ByteBuffer(0, ByteBuffer.BIG_ENDIAN); // don't send anything back
}
this._sendControl(WS13.FrameType.Control.Close, payload.flip().toBuffer());
this._closeExtensions(() => {
this._socket.end();
});
}
this._sendControl(WS13.FrameType.Control.Close, payload.flip().toBuffer());
this._socket.end();
}
this.state = WS13.State.Closed;
this.state = WS13.State.Closed;
this.emit('disconnected', code, reason, state == WS13.State.Closing);
if (state != WS13.State.ClosingError) {
this.emit('disconnected', code, reason, state == WS13.State.Closing);
}
break;
break;
case WS13.FrameType.Control.Ping:
this._sendControl(WS13.FrameType.Control.Pong, frame.payload);
break;
case WS13.FrameType.Control.Ping:
this._sendControl(WS13.FrameType.Control.Pong, frame.payload);
break;
case WS13.FrameType.Control.Pong:
if (frame.payload && frame.payload.length == 4 && frame.payload.readUInt32BE(0) == this._pingValue) {
this.emit('latency', Date.now() - this._pingTime);
this._pingFailures = 0;
this._queuePing();
}
case WS13.FrameType.Control.Pong:
if (frame.payload && frame.payload.length == 4 && frame.payload.readUInt32BE(0) == this._pingValue) {
this.emit('latency', Date.now() - this._pingTime);
this._pingFailures = 0;
this._queuePing();
}
break;
break;
default:
this._terminateError(WS13.StatusCode.UnacceptableDataType, "Unknown control frame type " + frame.opcode.toString(16).toUpperCase());
}
default:
this._terminateError(WS13.StatusCode.UnacceptableDataType, "Unknown control frame type " + frame.opcode.toString(16).toUpperCase());
}
});

@@ -355,3 +383,3 @@ return;

var dispatch = this.listenerCount('streamedMessage') >= 1;
var dispatch = this.listenerCount('streamedMessage') >= 1 && !frame.RSV1 && !frame.RSV2 && !frame.RSV3;
this._incomingStream = new StreamedIncomingMessage(frame, dispatch);

@@ -393,23 +421,32 @@

WebSocketBase.prototype._dispatchDataFrame = function(frame) {
switch (frame.opcode) {
case WS13.FrameType.Data.Text:
var utf8 = frame.payload.toString('utf8');
this.extensions.processIncomingMessage(getExtensionMessage(frame), (err, msg) => {
if (err) {
this._terminateError(WS13.StatusCode.ProtocolError, err.message || err);
return;
}
// Check that the UTF-8 is valid
if (Buffer.compare(new Buffer(utf8, 'utf8'), frame.payload) !== 0) {
// This is invalid. We must tear down the connection.
this._terminateError(WS13.StatusCode.InconsistentData, "Received invalid UTF-8 data in a text frame.");
return;
}
frame = fromExtensionMessage(msg);
this.emit('message', WS13.FrameType.Data.Text, utf8);
break;
switch (frame.opcode) {
case WS13.FrameType.Data.Text:
var utf8 = frame.payload.toString('utf8');
case WS13.FrameType.Data.Binary:
this.emit('message', WS13.FrameType.Data.Binary, frame.payload);
break;
// Check that the UTF-8 is valid
if (Buffer.compare(new Buffer(utf8, 'utf8'), frame.payload) !== 0) {
// This is invalid. We must tear down the connection.
this._terminateError(WS13.StatusCode.InconsistentData, "Received invalid UTF-8 data in a text frame.");
return;
}
default:
this._terminateError(WS13.StatusCode.UnacceptableDataType, "Unknown data frame type " + frame.opcode.toString(16).toUpperCase());
}
this.emit('message', WS13.FrameType.Data.Text, utf8);
break;
case WS13.FrameType.Data.Binary:
this.emit('message', WS13.FrameType.Data.Binary, frame.payload);
break;
default:
this._terminateError(WS13.StatusCode.UnacceptableDataType, "Unknown data frame type " + frame.opcode.toString(16).toUpperCase());
}
});
};

@@ -429,2 +466,6 @@

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

@@ -434,88 +475,139 @@ }

frame.payload = frame.payload || new Buffer(0);
var maskKey = frame.maskKey;
var fin = frame.FIN;
if (frame.payload.length == 0) {
frame.maskKey = null;
frame.maskKey = maskKey = null;
}
this.emit('debug', (bypassQueue ? "Sending" : "Queueing") + " frame " + frame.opcode.toString(16).toUpperCase() + ", " + (frame.FIN ? "FIN, " : "") +
(frame.maskKey ? "MASK, " : "") + "payload " + frame.payload.length + " bytes");
if (isControl || !frame.FIN || frame.opcode == 0) {
// https://github.com/faye/permessage-deflate-node/issues/6
onExtensionsProcessed.call(this, frame);
} else {
if (!bypassQueue) {
var queueId = ++this._extensionProcessingOutgoingFrameId;
this._outgoingFrames.push(queueId);
var size = 0;
size += 1; // FIN, RSV1, RSV2, RSV3, opcode
size += 1; // MASK, payload length
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;
}
}
if (frame.payload.length >= 126 && frame.payload.length <= 65535) {
size += 2; // 16-bit payload length
} else if (frame.payload.length > 65535) {
size += 8; // 64-bit payload length
}
this.extensions.processOutgoingMessage(getExtensionMessage(frame), (err, msg) => {
if (err) {
this._terminateError(WS13.StatusCode.ProtocolError, err.message || err);
return;
}
if (frame.maskKey) {
size += 4;
frame = fromExtensionMessage(msg);
frame.maskKey = maskKey;
frame.FIN = fin;
onExtensionsProcessed.call(this, frame);
});
}
size += frame.payload.length;
function onExtensionsProcessed(frame) {
var debugMsg = (bypassQueue ? "Sending" : "Queueing") + " frame " + frame.opcode.toString(16).toUpperCase() + ", " + (frame.FIN ? "FIN, " : "");
for (var i = 1; i <= 3; i++) {
if (frame['RSV' + i]) {
debugMsg += "RSV" + i + ", ";
}
}
var buf = new ByteBuffer(size, ByteBuffer.BIG_ENDIAN);
var byte = 0;
debugMsg += (frame.maskKey ? "MASK, " : "") + "payload " + frame.payload.length + " bytes";
this.emit('debug', debugMsg);
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);
var size = 0;
size += 1; // FIN, RSV1, RSV2, RSV3, opcode
size += 1; // MASK, payload length
byte = 0;
byte |= (frame.maskKey ? 1 : 0) << 7;
if (frame.payload.length >= 126 && frame.payload.length <= 65535) {
size += 2; // 16-bit payload length
} else if (frame.payload.length > 65535) {
size += 8; // 64-bit payload length
}
if (frame.payload.length <= 125) {
byte |= frame.payload.length;
if (frame.maskKey) {
size += 4;
}
size += frame.payload.length;
var buf = new ByteBuffer(size, ByteBuffer.BIG_ENDIAN);
var 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);
} 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);
}
byte = 0;
byte |= (frame.maskKey ? 1 : 0) << 7;
if (bypassQueue) {
this._socket.write(buf.flip().toBuffer());
} else {
this._outgoingFrames.push(buf.flip().toBuffer());
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);
}
if (bypassQueue) {
this._socket.write(buf.flip().toBuffer());
} else if (queueId) {
// This already has a placeholder in the queue
var idx = this._outgoingFrames.indexOf(queueId);
if (idx == -1) {
this._outgoingFrames.push(buf.flip().toBuffer());
} else {
this._outgoingFrames[idx] = buf.flip().toBuffer();
}
} else {
// No queue placeholder, just stick it in
this._outgoingFrames.push(buf.flip().toBuffer());
}
this._processQueue();
}
this._processQueue();
};
WebSocketBase.prototype._processQueue = function() {
var frame;
var frames = this._outgoingFrames.slice(0);
while (frames.length > 0) {
frame = frames.splice(0, 1)[0];
if (typeof frames[0] === 'number') {
// This is a placeholder, so we're done
console.log("Abandonining queue as we hit " + frames[0]);
break;
}
if (frame instanceof StreamedOutgoingMessage) {
if (!frame.started) {
if (frames[0] instanceof StreamedOutgoingMessage) {
if (!frames[0].started) {
this.emit('debug', "Starting StreamedOutgoingMessage");
frame._start();
frames[0]._start();
}
if (frame.finished) {
if (frames[0].finished) {
frames.splice(0, 1);
continue;
}
return;
break;
}
this._socket.write(frame);
this._socket.write(frames.splice(0, 1)[0]);
}

@@ -545,2 +637,3 @@

this.state = WS13.State.Closed;
this._closeExtensions();

@@ -564,5 +657,14 @@ if (this._socket) {

err.code = code;
this.state = WS13.State.ClosingError;
this.emit('error', err);
};
WebSocketBase.prototype._closeExtensions = function(callback) {
try {
this.extensions.close(callback);
} catch (ex) {
callback();
}
};
// Util

@@ -578,2 +680,36 @@ function maskOrUnmask(data, maskKey) {

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,
"payload": msg.data
};
}

@@ -67,2 +67,11 @@ var WS13 = require('./index.js');

if (this.options.extensions) {
this.extensions = this.options.extensions;
}
var extOffer = this.extensions.generateOffer();
if (extOffer) {
this.headers['sec-websocket-extensions'] = extOffer;
}
if (this.options.protocols) {

@@ -163,14 +172,2 @@ this.options.protocols = this.options.protocols.map(protocol => protocol.trim().toLowerCase());

if (headers['sec-websocket-extensions']) {
var extensions = headers['sec-websocket-extensions'].split(',').map(item => item.trim().toLowerCase());
var unsupported = extensions.filter(extension => this.extensions.indexOf(extension) == -1);
if (unsupported.length > 0) {
err.message = "Server is using unsupported extension" + (unsupported.length > 1 ? "s" : "") + unsupported.join(', ');
this._closeError(err);
return;
}
this.extensions = extensions;
}
if (headers['sec-websocket-protocol']) {

@@ -187,2 +184,10 @@ var protocol = headers['sec-websocket-protocol'].toLowerCase();

try {
this.extensions.activate(headers['sec-websocket-extensions']);
} catch (ex) {
err.message = ex.message;
this._closeError(err);
return;
}
this._socket = socket;

@@ -189,0 +194,0 @@ this._prepSocketEvents();

var WS13 = require('./index.js');
var Extensions = require('websocket-extensions');
var WebSocketBase = require('./base.js');

@@ -79,2 +80,6 @@ var HTTPStatusCodes = require('../resources/HTTPStatusCodes.json');

var extensions = new Extensions();
extensions.add(require('permessage-deflate'));
var selectedExtensions = extensions.generateResponse(req.headers['sec-websocket-extensions']);
var handshakeData = {

@@ -86,4 +91,5 @@ "path": uri.pathname,

"origin": req.headers.origin || null,
"extensions": [],
"selectedExtensions": [],
"extensions": req.headers['sec-websocket-extensions'],
"extensionsHandler": extensions,
"selectedExtensions": selectedExtensions,
"protocols": protocols || [],

@@ -129,2 +135,9 @@ "selectedProtocol": selectedProtocol || null,

var options = {
"pingInterval": this.options.pingInterval,
"pingTimeout": this.options.pingTimeout,
"pingFailures": this.options.pingFailures,
"extensions": extensions
};
headers.Upgrade = "websocket";

@@ -139,2 +152,11 @@ headers.Connection = "Upgrade";

if (response.extensions) {
options.extensions = extensions = response.extensions;
selectedExtensions = extensions.generateResponse(req.headers['sec-websocket-extensions']);
}
if (selectedExtensions) {
headers['Sec-WebSocket-Extensions'] = selectedExtensions;
}
if (handshakeData.selectedProtocol) {

@@ -146,8 +168,2 @@ headers['Sec-WebSocket-Protocol'] = handshakeData.selectedProtocol;

var options = {
"pingInterval": this.options.pingInterval,
"pingTimeout": this.options.pingTimeout,
"pingFailures": this.options.pingFailures
};
response.options = response.options || {};

@@ -217,3 +233,3 @@ for (var i in response.options) {

this.handshakeData = handshakeData;
this.extensions = handshakeData.selectedExtensions || [];
this.extensions = options.extensions;
this.protocol = handshakeData.selectedProtocol || null;

@@ -220,0 +236,0 @@

{
"name": "websocket13",
"version": "1.1.2",
"version": "1.2.0-beta1",
"description": "Simple WebSocket protocol 13 client with no native or heavy dependencies",

@@ -28,3 +28,5 @@ "author": "Alexander Corn <mckay@doctormckay.com>",

"dependencies": {
"bytebuffer": "^5.0.0"
"bytebuffer": "^5.0.0",
"websocket-extensions": "^0.1.1",
"permessage-deflate": "^0.1.5"
},

@@ -31,0 +33,0 @@ "engines": {

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc