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

engine.io-client

Package Overview
Dependencies
Maintainers
2
Versions
159
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

engine.io-client - npm Package Compare versions

Comparing version 3.4.0 to 4.0.0-alpha.0

CHANGELOG.md

16

lib/index.js

@@ -0,10 +1,14 @@

const Socket = require("./socket");
module.exports = require('./socket');
module.exports = (uri, opts) => new Socket(uri, opts);
/**
* Exports parser
*
* @api public
*
* Expose deps for legacy compatibility
* and standalone browser access.
*/
module.exports.parser = require('engine.io-parser');
module.exports.Socket = Socket;
module.exports.protocol = Socket.protocol; // this is an int
module.exports.Transport = require("./transport");
module.exports.transports = require("./transports/index");
module.exports.parser = require("engine.io-parser");

@@ -1,748 +0,673 @@

/**
* Module dependencies.
*/
const transports = require("./transports/index");
const Emitter = require("component-emitter");
const debug = require("debug")("engine.io-client:socket");
const index = require("indexof");
const parser = require("engine.io-parser");
const parseuri = require("parseuri");
const parseqs = require("parseqs");
var transports = require('./transports/index');
var Emitter = require('component-emitter');
var debug = require('debug')('engine.io-client:socket');
var index = require('indexof');
var parser = require('engine.io-parser');
var parseuri = require('parseuri');
var parseqs = require('parseqs');
class Socket extends Emitter {
/**
* Socket constructor.
*
* @param {String|Object} uri or options
* @param {Object} options
* @api public
*/
constructor(uri, opts = {}) {
super();
/**
* Module exports.
*/
if (uri && "object" === typeof uri) {
opts = uri;
uri = null;
}
module.exports = Socket;
if (uri) {
uri = parseuri(uri);
opts.hostname = uri.host;
opts.secure = uri.protocol === "https" || uri.protocol === "wss";
opts.port = uri.port;
if (uri.query) opts.query = uri.query;
} else if (opts.host) {
opts.hostname = parseuri(opts.host).host;
}
/**
* Socket constructor.
*
* @param {String|Object} uri or options
* @param {Object} options
* @api public
*/
this.secure =
null != opts.secure
? opts.secure
: typeof location !== "undefined" && "https:" === location.protocol;
function Socket (uri, opts) {
if (!(this instanceof Socket)) return new Socket(uri, opts);
if (opts.hostname && !opts.port) {
// if no port is specified manually, use the protocol default
opts.port = this.secure ? "443" : "80";
}
opts = opts || {};
this.hostname =
opts.hostname ||
(typeof location !== "undefined" ? location.hostname : "localhost");
this.port =
opts.port ||
(typeof location !== "undefined" && location.port
? location.port
: this.secure
? 443
: 80);
if (uri && 'object' === typeof uri) {
opts = uri;
uri = null;
}
this.transports = opts.transports || ["polling", "websocket"];
this.readyState = "";
this.writeBuffer = [];
this.prevBufferLen = 0;
if (uri) {
uri = parseuri(uri);
opts.hostname = uri.host;
opts.secure = uri.protocol === 'https' || uri.protocol === 'wss';
opts.port = uri.port;
if (uri.query) opts.query = uri.query;
} else if (opts.host) {
opts.hostname = parseuri(opts.host).host;
}
this.opts = Object.assign(
{
path: "/engine.io",
agent: false,
upgrade: true,
jsonp: true,
timestampParam: "t",
policyPort: 843,
rememberUpgrade: false,
rejectUnauthorized: true,
perMessageDeflate: {
threshold: 1024
},
transportOptions: {}
},
opts
);
this.secure = null != opts.secure ? opts.secure
: (typeof location !== 'undefined' && 'https:' === location.protocol);
this.opts.path = this.opts.path.replace(/\/$/, "") + "/";
if (opts.hostname && !opts.port) {
// if no port is specified manually, use the protocol default
opts.port = this.secure ? '443' : '80';
}
if (typeof this.opts.query === "string") {
this.opts.query = parseqs.decode(this.opts.query);
}
this.agent = opts.agent || false;
this.hostname = opts.hostname ||
(typeof location !== 'undefined' ? location.hostname : 'localhost');
this.port = opts.port || (typeof location !== 'undefined' && location.port
? location.port
: (this.secure ? 443 : 80));
this.query = opts.query || {};
if ('string' === typeof this.query) this.query = parseqs.decode(this.query);
this.upgrade = false !== opts.upgrade;
this.path = (opts.path || '/engine.io').replace(/\/$/, '') + '/';
this.forceJSONP = !!opts.forceJSONP;
this.jsonp = false !== opts.jsonp;
this.forceBase64 = !!opts.forceBase64;
this.enablesXDR = !!opts.enablesXDR;
this.withCredentials = false !== opts.withCredentials;
this.timestampParam = opts.timestampParam || 't';
this.timestampRequests = opts.timestampRequests;
this.transports = opts.transports || ['polling', 'websocket'];
this.transportOptions = opts.transportOptions || {};
this.readyState = '';
this.writeBuffer = [];
this.prevBufferLen = 0;
this.policyPort = opts.policyPort || 843;
this.rememberUpgrade = opts.rememberUpgrade || false;
this.binaryType = null;
this.onlyBinaryUpgrades = opts.onlyBinaryUpgrades;
this.perMessageDeflate = false !== opts.perMessageDeflate ? (opts.perMessageDeflate || {}) : false;
// detect ReactNative environment
this.opts.isReactNative =
typeof navigator !== "undefined" &&
typeof navigator.product === "string" &&
navigator.product.toLowerCase() === "reactnative";
if (true === this.perMessageDeflate) this.perMessageDeflate = {};
if (this.perMessageDeflate && null == this.perMessageDeflate.threshold) {
this.perMessageDeflate.threshold = 1024;
}
// set on handshake
this.id = null;
this.upgrades = null;
this.pingInterval = null;
this.pingTimeout = null;
// SSL options for Node.js client
this.pfx = opts.pfx || null;
this.key = opts.key || null;
this.passphrase = opts.passphrase || null;
this.cert = opts.cert || null;
this.ca = opts.ca || null;
this.ciphers = opts.ciphers || null;
this.rejectUnauthorized = opts.rejectUnauthorized === undefined ? true : opts.rejectUnauthorized;
this.forceNode = !!opts.forceNode;
// set on heartbeat
this.pingTimeoutTimer = null;
// detect ReactNative environment
this.isReactNative = (typeof navigator !== 'undefined' && typeof navigator.product === 'string' && navigator.product.toLowerCase() === 'reactnative');
this.open();
}
// other options for Node.js or ReactNative client
if (typeof self === 'undefined' || this.isReactNative) {
if (opts.extraHeaders && Object.keys(opts.extraHeaders).length > 0) {
this.extraHeaders = opts.extraHeaders;
}
/**
* Creates transport of the given type.
*
* @param {String} transport name
* @return {Transport}
* @api private
*/
createTransport(name) {
debug('creating transport "%s"', name);
const query = clone(this.opts.query);
if (opts.localAddress) {
this.localAddress = opts.localAddress;
}
}
// append engine.io protocol identifier
query.EIO = parser.protocol;
// set on handshake
this.id = null;
this.upgrades = null;
this.pingInterval = null;
this.pingTimeout = null;
// transport name
query.transport = name;
// set on heartbeat
this.pingIntervalTimer = null;
this.pingTimeoutTimer = null;
// session id if we already have one
if (this.id) query.sid = this.id;
this.open();
}
const opts = Object.assign(
{
query,
socket: this,
hostname: this.hostname,
secure: this.secure,
port: this.port
},
this.opts.transportOptions[name],
this.opts
);
Socket.priorWebsocketSuccess = false;
// console.log(opts);
debug("options: %j", opts);
/**
* Mix in `Emitter`.
*/
return new transports[name](opts);
}
Emitter(Socket.prototype);
/**
* Initializes transport to use and starts probe.
*
* @api private
*/
open() {
let transport;
if (
this.opts.rememberUpgrade &&
Socket.priorWebsocketSuccess &&
this.transports.indexOf("websocket") !== -1
) {
transport = "websocket";
} else if (0 === this.transports.length) {
// Emit error on next tick so it can be listened to
const self = this;
setTimeout(function() {
self.emit("error", "No transports available");
}, 0);
return;
} else {
transport = this.transports[0];
}
this.readyState = "opening";
/**
* Protocol version.
*
* @api public
*/
// Retry with the next transport if the transport is disabled (jsonp: false)
try {
transport = this.createTransport(transport);
} catch (e) {
debug("error while creating transport: %s", e);
this.transports.shift();
this.open();
return;
}
Socket.protocol = parser.protocol; // this is an int
transport.open();
this.setTransport(transport);
}
/**
* Expose deps for legacy compatibility
* and standalone browser access.
*/
/**
* Sets the current transport. Disables the existing one (if any).
*
* @api private
*/
setTransport(transport) {
debug("setting transport %s", transport.name);
const self = this;
Socket.Socket = Socket;
Socket.Transport = require('./transport');
Socket.transports = require('./transports/index');
Socket.parser = require('engine.io-parser');
if (this.transport) {
debug("clearing existing transport %s", this.transport.name);
this.transport.removeAllListeners();
}
/**
* Creates transport of the given type.
*
* @param {String} transport name
* @return {Transport}
* @api private
*/
// set up transport
this.transport = transport;
Socket.prototype.createTransport = function (name) {
debug('creating transport "%s"', name);
var query = clone(this.query);
// set up transport listeners
transport
.on("drain", function() {
self.onDrain();
})
.on("packet", function(packet) {
self.onPacket(packet);
})
.on("error", function(e) {
self.onError(e);
})
.on("close", function() {
self.onClose("transport close");
});
}
// append engine.io protocol identifier
query.EIO = parser.protocol;
/**
* Probes a transport.
*
* @param {String} transport name
* @api private
*/
probe(name) {
debug('probing transport "%s"', name);
let transport = this.createTransport(name, { probe: 1 });
let failed = false;
const self = this;
// transport name
query.transport = name;
Socket.priorWebsocketSuccess = false;
// per-transport options
var options = this.transportOptions[name] || {};
function onTransportOpen() {
if (self.onlyBinaryUpgrades) {
const upgradeLosesBinary =
!this.supportsBinary && self.transport.supportsBinary;
failed = failed || upgradeLosesBinary;
}
if (failed) return;
// session id if we already have one
if (this.id) query.sid = this.id;
debug('probe transport "%s" opened', name);
transport.send([{ type: "ping", data: "probe" }]);
transport.once("packet", function(msg) {
if (failed) return;
if ("pong" === msg.type && "probe" === msg.data) {
debug('probe transport "%s" pong', name);
self.upgrading = true;
self.emit("upgrading", transport);
if (!transport) return;
Socket.priorWebsocketSuccess = "websocket" === transport.name;
var transport = new transports[name]({
query: query,
socket: this,
agent: options.agent || this.agent,
hostname: options.hostname || this.hostname,
port: options.port || this.port,
secure: options.secure || this.secure,
path: options.path || this.path,
forceJSONP: options.forceJSONP || this.forceJSONP,
jsonp: options.jsonp || this.jsonp,
forceBase64: options.forceBase64 || this.forceBase64,
enablesXDR: options.enablesXDR || this.enablesXDR,
withCredentials: options.withCredentials || this.withCredentials,
timestampRequests: options.timestampRequests || this.timestampRequests,
timestampParam: options.timestampParam || this.timestampParam,
policyPort: options.policyPort || this.policyPort,
pfx: options.pfx || this.pfx,
key: options.key || this.key,
passphrase: options.passphrase || this.passphrase,
cert: options.cert || this.cert,
ca: options.ca || this.ca,
ciphers: options.ciphers || this.ciphers,
rejectUnauthorized: options.rejectUnauthorized || this.rejectUnauthorized,
perMessageDeflate: options.perMessageDeflate || this.perMessageDeflate,
extraHeaders: options.extraHeaders || this.extraHeaders,
forceNode: options.forceNode || this.forceNode,
localAddress: options.localAddress || this.localAddress,
requestTimeout: options.requestTimeout || this.requestTimeout,
protocols: options.protocols || void (0),
isReactNative: this.isReactNative
});
debug('pausing current transport "%s"', self.transport.name);
self.transport.pause(function() {
if (failed) return;
if ("closed" === self.readyState) return;
debug("changing transport and sending upgrade packet");
return transport;
};
cleanup();
function clone (obj) {
var o = {};
for (var i in obj) {
if (obj.hasOwnProperty(i)) {
o[i] = obj[i];
self.setTransport(transport);
transport.send([{ type: "upgrade" }]);
self.emit("upgrade", transport);
transport = null;
self.upgrading = false;
self.flush();
});
} else {
debug('probe transport "%s" failed', name);
const err = new Error("probe error");
err.transport = transport.name;
self.emit("upgradeError", err);
}
});
}
}
return o;
}
/**
* Initializes transport to use and starts probe.
*
* @api private
*/
Socket.prototype.open = function () {
var transport;
if (this.rememberUpgrade && Socket.priorWebsocketSuccess && this.transports.indexOf('websocket') !== -1) {
transport = 'websocket';
} else if (0 === this.transports.length) {
// Emit error on next tick so it can be listened to
var self = this;
setTimeout(function () {
self.emit('error', 'No transports available');
}, 0);
return;
} else {
transport = this.transports[0];
}
this.readyState = 'opening';
function freezeTransport() {
if (failed) return;
// Retry with the next transport if the transport is disabled (jsonp: false)
try {
transport = this.createTransport(transport);
} catch (e) {
this.transports.shift();
this.open();
return;
}
// Any callback called by transport should be ignored since now
failed = true;
transport.open();
this.setTransport(transport);
};
cleanup();
/**
* Sets the current transport. Disables the existing one (if any).
*
* @api private
*/
transport.close();
transport = null;
}
Socket.prototype.setTransport = function (transport) {
debug('setting transport %s', transport.name);
var self = this;
// Handle any error that happens while probing
function onerror(err) {
const error = new Error("probe error: " + err);
error.transport = transport.name;
if (this.transport) {
debug('clearing existing transport %s', this.transport.name);
this.transport.removeAllListeners();
}
freezeTransport();
// set up transport
this.transport = transport;
debug('probe transport "%s" failed because of error: %s', name, err);
// set up transport listeners
transport
.on('drain', function () {
self.onDrain();
})
.on('packet', function (packet) {
self.onPacket(packet);
})
.on('error', function (e) {
self.onError(e);
})
.on('close', function () {
self.onClose('transport close');
});
};
self.emit("upgradeError", error);
}
/**
* Probes a transport.
*
* @param {String} transport name
* @api private
*/
function onTransportClose() {
onerror("transport closed");
}
Socket.prototype.probe = function (name) {
debug('probing transport "%s"', name);
var transport = this.createTransport(name, { probe: 1 });
var failed = false;
var self = this;
// When the socket is closed while we're probing
function onclose() {
onerror("socket closed");
}
Socket.priorWebsocketSuccess = false;
// When the socket is upgraded while we're probing
function onupgrade(to) {
if (transport && to.name !== transport.name) {
debug('"%s" works - aborting "%s"', to.name, transport.name);
freezeTransport();
}
}
function onTransportOpen () {
if (self.onlyBinaryUpgrades) {
var upgradeLosesBinary = !this.supportsBinary && self.transport.supportsBinary;
failed = failed || upgradeLosesBinary;
// Remove all listeners on the transport and on self
function cleanup() {
transport.removeListener("open", onTransportOpen);
transport.removeListener("error", onerror);
transport.removeListener("close", onTransportClose);
self.removeListener("close", onclose);
self.removeListener("upgrading", onupgrade);
}
if (failed) return;
debug('probe transport "%s" opened', name);
transport.send([{ type: 'ping', data: 'probe' }]);
transport.once('packet', function (msg) {
if (failed) return;
if ('pong' === msg.type && 'probe' === msg.data) {
debug('probe transport "%s" pong', name);
self.upgrading = true;
self.emit('upgrading', transport);
if (!transport) return;
Socket.priorWebsocketSuccess = 'websocket' === transport.name;
transport.once("open", onTransportOpen);
transport.once("error", onerror);
transport.once("close", onTransportClose);
debug('pausing current transport "%s"', self.transport.name);
self.transport.pause(function () {
if (failed) return;
if ('closed' === self.readyState) return;
debug('changing transport and sending upgrade packet');
this.once("close", onclose);
this.once("upgrading", onupgrade);
cleanup();
self.setTransport(transport);
transport.send([{ type: 'upgrade' }]);
self.emit('upgrade', transport);
transport = null;
self.upgrading = false;
self.flush();
});
} else {
debug('probe transport "%s" failed', name);
var err = new Error('probe error');
err.transport = transport.name;
self.emit('upgradeError', err);
}
});
transport.open();
}
function freezeTransport () {
if (failed) return;
/**
* Called when connection is deemed open.
*
* @api public
*/
onOpen() {
debug("socket open");
this.readyState = "open";
Socket.priorWebsocketSuccess = "websocket" === this.transport.name;
this.emit("open");
this.flush();
// Any callback called by transport should be ignored since now
failed = true;
cleanup();
transport.close();
transport = null;
// we check for `readyState` in case an `open`
// listener already closed the socket
if (
"open" === this.readyState &&
this.opts.upgrade &&
this.transport.pause
) {
debug("starting upgrade probes");
let i = 0;
const l = this.upgrades.length;
for (; i < l; i++) {
this.probe(this.upgrades[i]);
}
}
}
// Handle any error that happens while probing
function onerror (err) {
var error = new Error('probe error: ' + err);
error.transport = transport.name;
/**
* Handles a packet.
*
* @api private
*/
onPacket(packet) {
if (
"opening" === this.readyState ||
"open" === this.readyState ||
"closing" === this.readyState
) {
debug('socket receive: type "%s", data "%s"', packet.type, packet.data);
freezeTransport();
this.emit("packet", packet);
debug('probe transport "%s" failed because of error: %s', name, err);
// Socket is live - any packet counts
this.emit("heartbeat");
self.emit('upgradeError', error);
}
switch (packet.type) {
case "open":
this.onHandshake(JSON.parse(packet.data));
break;
function onTransportClose () {
onerror('transport closed');
}
case "ping":
this.resetPingTimeout();
this.sendPacket("pong");
this.emit("pong");
break;
// When the socket is closed while we're probing
function onclose () {
onerror('socket closed');
}
case "error":
const err = new Error("server error");
err.code = packet.data;
this.onError(err);
break;
// When the socket is upgraded while we're probing
function onupgrade (to) {
if (transport && to.name !== transport.name) {
debug('"%s" works - aborting "%s"', to.name, transport.name);
freezeTransport();
case "message":
this.emit("data", packet.data);
this.emit("message", packet.data);
break;
}
} else {
debug('packet received with socket readyState "%s"', this.readyState);
}
}
// Remove all listeners on the transport and on self
function cleanup () {
transport.removeListener('open', onTransportOpen);
transport.removeListener('error', onerror);
transport.removeListener('close', onTransportClose);
self.removeListener('close', onclose);
self.removeListener('upgrading', onupgrade);
/**
* Called upon handshake completion.
*
* @param {Object} handshake obj
* @api private
*/
onHandshake(data) {
this.emit("handshake", data);
this.id = data.sid;
this.transport.query.sid = data.sid;
this.upgrades = this.filterUpgrades(data.upgrades);
this.pingInterval = data.pingInterval;
this.pingTimeout = data.pingTimeout;
this.onOpen();
// In case open handler closes socket
if ("closed" === this.readyState) return;
this.resetPingTimeout();
}
transport.once('open', onTransportOpen);
transport.once('error', onerror);
transport.once('close', onTransportClose);
/**
* Sets and resets ping timeout timer based on server pings.
*
* @api private
*/
resetPingTimeout() {
clearTimeout(this.pingTimeoutTimer);
this.pingTimeoutTimer = setTimeout(() => {
this.onClose("ping timeout");
}, this.pingInterval + this.pingTimeout);
}
this.once('close', onclose);
this.once('upgrading', onupgrade);
/**
* Called on `drain` event
*
* @api private
*/
onDrain() {
this.writeBuffer.splice(0, this.prevBufferLen);
transport.open();
};
// setting prevBufferLen = 0 is very important
// for example, when upgrading, upgrade packet is sent over,
// and a nonzero prevBufferLen could cause problems on `drain`
this.prevBufferLen = 0;
/**
* Called when connection is deemed open.
*
* @api public
*/
Socket.prototype.onOpen = function () {
debug('socket open');
this.readyState = 'open';
Socket.priorWebsocketSuccess = 'websocket' === this.transport.name;
this.emit('open');
this.flush();
// we check for `readyState` in case an `open`
// listener already closed the socket
if ('open' === this.readyState && this.upgrade && this.transport.pause) {
debug('starting upgrade probes');
for (var i = 0, l = this.upgrades.length; i < l; i++) {
this.probe(this.upgrades[i]);
if (0 === this.writeBuffer.length) {
this.emit("drain");
} else {
this.flush();
}
}
};
/**
* Handles a packet.
*
* @api private
*/
Socket.prototype.onPacket = function (packet) {
if ('opening' === this.readyState || 'open' === this.readyState ||
'closing' === this.readyState) {
debug('socket receive: type "%s", data "%s"', packet.type, packet.data);
this.emit('packet', packet);
// Socket is live - any packet counts
this.emit('heartbeat');
switch (packet.type) {
case 'open':
this.onHandshake(JSON.parse(packet.data));
break;
case 'pong':
this.setPing();
this.emit('pong');
break;
case 'error':
var err = new Error('server error');
err.code = packet.data;
this.onError(err);
break;
case 'message':
this.emit('data', packet.data);
this.emit('message', packet.data);
break;
/**
* Flush write buffers.
*
* @api private
*/
flush() {
if (
"closed" !== this.readyState &&
this.transport.writable &&
!this.upgrading &&
this.writeBuffer.length
) {
debug("flushing %d packets in socket", this.writeBuffer.length);
this.transport.send(this.writeBuffer);
// keep track of current length of writeBuffer
// splice writeBuffer and callbackBuffer on `drain`
this.prevBufferLen = this.writeBuffer.length;
this.emit("flush");
}
} else {
debug('packet received with socket readyState "%s"', this.readyState);
}
};
/**
* Called upon handshake completion.
*
* @param {Object} handshake obj
* @api private
*/
/**
* Sends a message.
*
* @param {String} message.
* @param {Function} callback function.
* @param {Object} options.
* @return {Socket} for chaining.
* @api public
*/
write(msg, options, fn) {
this.sendPacket("message", msg, options, fn);
return this;
}
Socket.prototype.onHandshake = function (data) {
this.emit('handshake', data);
this.id = data.sid;
this.transport.query.sid = data.sid;
this.upgrades = this.filterUpgrades(data.upgrades);
this.pingInterval = data.pingInterval;
this.pingTimeout = data.pingTimeout;
this.onOpen();
// In case open handler closes socket
if ('closed' === this.readyState) return;
this.setPing();
send(msg, options, fn) {
this.sendPacket("message", msg, options, fn);
return this;
}
// Prolong liveness of socket on heartbeat
this.removeListener('heartbeat', this.onHeartbeat);
this.on('heartbeat', this.onHeartbeat);
};
/**
* Sends a packet.
*
* @param {String} packet type.
* @param {String} data.
* @param {Object} options.
* @param {Function} callback function.
* @api private
*/
sendPacket(type, data, options, fn) {
if ("function" === typeof data) {
fn = data;
data = undefined;
}
/**
* Resets ping timeout.
*
* @api private
*/
if ("function" === typeof options) {
fn = options;
options = null;
}
Socket.prototype.onHeartbeat = function (timeout) {
clearTimeout(this.pingTimeoutTimer);
var self = this;
self.pingTimeoutTimer = setTimeout(function () {
if ('closed' === self.readyState) return;
self.onClose('ping timeout');
}, timeout || (self.pingInterval + self.pingTimeout));
};
if ("closing" === this.readyState || "closed" === this.readyState) {
return;
}
/**
* Pings server every `this.pingInterval` and expects response
* within `this.pingTimeout` or closes connection.
*
* @api private
*/
options = options || {};
options.compress = false !== options.compress;
Socket.prototype.setPing = function () {
var self = this;
clearTimeout(self.pingIntervalTimer);
self.pingIntervalTimer = setTimeout(function () {
debug('writing ping packet - expecting pong within %sms', self.pingTimeout);
self.ping();
self.onHeartbeat(self.pingTimeout);
}, self.pingInterval);
};
/**
* Sends a ping packet.
*
* @api private
*/
Socket.prototype.ping = function () {
var self = this;
this.sendPacket('ping', function () {
self.emit('ping');
});
};
/**
* Called on `drain` event
*
* @api private
*/
Socket.prototype.onDrain = function () {
this.writeBuffer.splice(0, this.prevBufferLen);
// setting prevBufferLen = 0 is very important
// for example, when upgrading, upgrade packet is sent over,
// and a nonzero prevBufferLen could cause problems on `drain`
this.prevBufferLen = 0;
if (0 === this.writeBuffer.length) {
this.emit('drain');
} else {
const packet = {
type: type,
data: data,
options: options
};
this.emit("packetCreate", packet);
this.writeBuffer.push(packet);
if (fn) this.once("flush", fn);
this.flush();
}
};
/**
* Flush write buffers.
*
* @api private
*/
/**
* Closes the connection.
*
* @api private
*/
close() {
const self = this;
Socket.prototype.flush = function () {
if ('closed' !== this.readyState && this.transport.writable &&
!this.upgrading && this.writeBuffer.length) {
debug('flushing %d packets in socket', this.writeBuffer.length);
this.transport.send(this.writeBuffer);
// keep track of current length of writeBuffer
// splice writeBuffer and callbackBuffer on `drain`
this.prevBufferLen = this.writeBuffer.length;
this.emit('flush');
}
};
if ("opening" === this.readyState || "open" === this.readyState) {
this.readyState = "closing";
/**
* Sends a message.
*
* @param {String} message.
* @param {Function} callback function.
* @param {Object} options.
* @return {Socket} for chaining.
* @api public
*/
if (this.writeBuffer.length) {
this.once("drain", function() {
if (this.upgrading) {
waitForUpgrade();
} else {
close();
}
});
} else if (this.upgrading) {
waitForUpgrade();
} else {
close();
}
}
Socket.prototype.write =
Socket.prototype.send = function (msg, options, fn) {
this.sendPacket('message', msg, options, fn);
return this;
};
function close() {
self.onClose("forced close");
debug("socket closing - telling transport to close");
self.transport.close();
}
/**
* Sends a packet.
*
* @param {String} packet type.
* @param {String} data.
* @param {Object} options.
* @param {Function} callback function.
* @api private
*/
function cleanupAndClose() {
self.removeListener("upgrade", cleanupAndClose);
self.removeListener("upgradeError", cleanupAndClose);
close();
}
Socket.prototype.sendPacket = function (type, data, options, fn) {
if ('function' === typeof data) {
fn = data;
data = undefined;
}
function waitForUpgrade() {
// wait for upgrade to finish since we can't send packets while pausing a transport
self.once("upgrade", cleanupAndClose);
self.once("upgradeError", cleanupAndClose);
}
if ('function' === typeof options) {
fn = options;
options = null;
return this;
}
if ('closing' === this.readyState || 'closed' === this.readyState) {
return;
/**
* Called upon transport error
*
* @api private
*/
onError(err) {
debug("socket error %j", err);
Socket.priorWebsocketSuccess = false;
this.emit("error", err);
this.onClose("transport error", err);
}
options = options || {};
options.compress = false !== options.compress;
/**
* Called upon transport close.
*
* @api private
*/
onClose(reason, desc) {
if (
"opening" === this.readyState ||
"open" === this.readyState ||
"closing" === this.readyState
) {
debug('socket close with reason: "%s"', reason);
const self = this;
var packet = {
type: type,
data: data,
options: options
};
this.emit('packetCreate', packet);
this.writeBuffer.push(packet);
if (fn) this.once('flush', fn);
this.flush();
};
// clear timers
clearTimeout(this.pingIntervalTimer);
clearTimeout(this.pingTimeoutTimer);
/**
* Closes the connection.
*
* @api private
*/
// stop event from firing again for transport
this.transport.removeAllListeners("close");
Socket.prototype.close = function () {
if ('opening' === this.readyState || 'open' === this.readyState) {
this.readyState = 'closing';
// ensure transport won't stay open
this.transport.close();
var self = this;
// ignore further transport communication
this.transport.removeAllListeners();
if (this.writeBuffer.length) {
this.once('drain', function () {
if (this.upgrading) {
waitForUpgrade();
} else {
close();
}
});
} else if (this.upgrading) {
waitForUpgrade();
} else {
close();
}
}
// set ready state
this.readyState = "closed";
function close () {
self.onClose('forced close');
debug('socket closing - telling transport to close');
self.transport.close();
}
// clear session id
this.id = null;
function cleanupAndClose () {
self.removeListener('upgrade', cleanupAndClose);
self.removeListener('upgradeError', cleanupAndClose);
close();
// emit close event
this.emit("close", reason, desc);
// clean buffers after, so users can still
// grab the buffers on `close` event
self.writeBuffer = [];
self.prevBufferLen = 0;
}
}
function waitForUpgrade () {
// wait for upgrade to finish since we can't send packets while pausing a transport
self.once('upgrade', cleanupAndClose);
self.once('upgradeError', cleanupAndClose);
/**
* Filters upgrades, returning only those matching client transports.
*
* @param {Array} server upgrades
* @api private
*
*/
filterUpgrades(upgrades) {
const filteredUpgrades = [];
let i = 0;
const j = upgrades.length;
for (; i < j; i++) {
if (~index(this.transports, upgrades[i]))
filteredUpgrades.push(upgrades[i]);
}
return filteredUpgrades;
}
}
return this;
};
Socket.priorWebsocketSuccess = false;
/**
* Called upon transport error
* Protocol version.
*
* @api private
* @api public
*/
Socket.prototype.onError = function (err) {
debug('socket error %j', err);
Socket.priorWebsocketSuccess = false;
this.emit('error', err);
this.onClose('transport error', err);
};
Socket.protocol = parser.protocol; // this is an int
/**
* Called upon transport close.
*
* @api private
*/
Socket.prototype.onClose = function (reason, desc) {
if ('opening' === this.readyState || 'open' === this.readyState || 'closing' === this.readyState) {
debug('socket close with reason: "%s"', reason);
var self = this;
// clear timers
clearTimeout(this.pingIntervalTimer);
clearTimeout(this.pingTimeoutTimer);
// stop event from firing again for transport
this.transport.removeAllListeners('close');
// ensure transport won't stay open
this.transport.close();
// ignore further transport communication
this.transport.removeAllListeners();
// set ready state
this.readyState = 'closed';
// clear session id
this.id = null;
// emit close event
this.emit('close', reason, desc);
// clean buffers after, so users can still
// grab the buffers on `close` event
self.writeBuffer = [];
self.prevBufferLen = 0;
function clone(obj) {
const o = {};
for (let i in obj) {
if (obj.hasOwnProperty(i)) {
o[i] = obj[i];
}
}
};
return o;
}
/**
* Filters upgrades, returning only those matching client transports.
*
* @param {Array} server upgrades
* @api private
*
*/
Socket.prototype.filterUpgrades = function (upgrades) {
var filteredUpgrades = [];
for (var i = 0, j = upgrades.length; i < j; i++) {
if (~index(this.transports, upgrades[i])) filteredUpgrades.push(upgrades[i]);
}
return filteredUpgrades;
};
module.exports = Socket;

@@ -1,161 +0,117 @@

/**
* Module dependencies.
*/
const parser = require("engine.io-parser");
const Emitter = require("component-emitter");
var parser = require('engine.io-parser');
var Emitter = require('component-emitter');
class Transport extends Emitter {
/**
* Transport abstract constructor.
*
* @param {Object} options.
* @api private
*/
constructor(opts) {
super();
/**
* Module exports.
*/
this.opts = opts;
this.query = opts.query;
this.readyState = "";
this.socket = opts.socket;
}
module.exports = Transport;
/**
* Emits an error.
*
* @param {String} str
* @return {Transport} for chaining
* @api public
*/
onError(msg, desc) {
const err = new Error(msg);
err.type = "TransportError";
err.description = desc;
this.emit("error", err);
return this;
}
/**
* Transport abstract constructor.
*
* @param {Object} options.
* @api private
*/
/**
* Opens the transport.
*
* @api public
*/
open() {
if ("closed" === this.readyState || "" === this.readyState) {
this.readyState = "opening";
this.doOpen();
}
function Transport (opts) {
this.path = opts.path;
this.hostname = opts.hostname;
this.port = opts.port;
this.secure = opts.secure;
this.query = opts.query;
this.timestampParam = opts.timestampParam;
this.timestampRequests = opts.timestampRequests;
this.readyState = '';
this.agent = opts.agent || false;
this.socket = opts.socket;
this.enablesXDR = opts.enablesXDR;
this.withCredentials = opts.withCredentials;
return this;
}
// SSL options for Node.js client
this.pfx = opts.pfx;
this.key = opts.key;
this.passphrase = opts.passphrase;
this.cert = opts.cert;
this.ca = opts.ca;
this.ciphers = opts.ciphers;
this.rejectUnauthorized = opts.rejectUnauthorized;
this.forceNode = opts.forceNode;
/**
* Closes the transport.
*
* @api private
*/
close() {
if ("opening" === this.readyState || "open" === this.readyState) {
this.doClose();
this.onClose();
}
// results of ReactNative environment detection
this.isReactNative = opts.isReactNative;
return this;
}
// other options for Node.js client
this.extraHeaders = opts.extraHeaders;
this.localAddress = opts.localAddress;
}
/**
* Sends multiple packets.
*
* @param {Array} packets
* @api private
*/
send(packets) {
if ("open" === this.readyState) {
this.write(packets);
} else {
throw new Error("Transport not open");
}
}
/**
* Mix in `Emitter`.
*/
/**
* Called upon open
*
* @api private
*/
onOpen() {
this.readyState = "open";
this.writable = true;
this.emit("open");
}
Emitter(Transport.prototype);
/**
* Emits an error.
*
* @param {String} str
* @return {Transport} for chaining
* @api public
*/
Transport.prototype.onError = function (msg, desc) {
var err = new Error(msg);
err.type = 'TransportError';
err.description = desc;
this.emit('error', err);
return this;
};
/**
* Opens the transport.
*
* @api public
*/
Transport.prototype.open = function () {
if ('closed' === this.readyState || '' === this.readyState) {
this.readyState = 'opening';
this.doOpen();
/**
* Called with data.
*
* @param {String} data
* @api private
*/
onData(data) {
const packet = parser.decodePacket(data, this.socket.binaryType);
this.onPacket(packet);
}
return this;
};
/**
* Closes the transport.
*
* @api private
*/
Transport.prototype.close = function () {
if ('opening' === this.readyState || 'open' === this.readyState) {
this.doClose();
this.onClose();
/**
* Called with a decoded packet.
*/
onPacket(packet) {
this.emit("packet", packet);
}
return this;
};
/**
* Sends multiple packets.
*
* @param {Array} packets
* @api private
*/
Transport.prototype.send = function (packets) {
if ('open' === this.readyState) {
this.write(packets);
} else {
throw new Error('Transport not open');
/**
* Called upon close.
*
* @api private
*/
onClose() {
this.readyState = "closed";
this.emit("close");
}
};
}
/**
* Called upon open
*
* @api private
*/
Transport.prototype.onOpen = function () {
this.readyState = 'open';
this.writable = true;
this.emit('open');
};
/**
* Called with data.
*
* @param {String} data
* @api private
*/
Transport.prototype.onData = function (data) {
var packet = parser.decodePacket(data, this.socket.binaryType);
this.onPacket(packet);
};
/**
* Called with a decoded packet.
*/
Transport.prototype.onPacket = function (packet) {
this.emit('packet', packet);
};
/**
* Called upon close.
*
* @api private
*/
Transport.prototype.onClose = function () {
this.readyState = 'closed';
this.emit('close');
};
module.exports = Transport;

@@ -1,14 +0,6 @@

/**
* Module dependencies
*/
const XMLHttpRequest = require("xmlhttprequest-ssl");
const XHR = require("./polling-xhr");
const JSONP = require("./polling-jsonp");
const websocket = require("./websocket");
var XMLHttpRequest = require('xmlhttprequest-ssl');
var XHR = require('./polling-xhr');
var JSONP = require('./polling-jsonp');
var websocket = require('./websocket');
/**
* Export transports.
*/
exports.polling = polling;

@@ -24,11 +16,11 @@ exports.websocket = websocket;

function polling (opts) {
var xhr;
var xd = false;
var xs = false;
var jsonp = false !== opts.jsonp;
function polling(opts) {
let xhr;
let xd = false;
let xs = false;
const jsonp = false !== opts.jsonp;
if (typeof location !== 'undefined') {
var isSSL = 'https:' === location.protocol;
var port = location.port;
if (typeof location !== "undefined") {
const isSSL = "https:" === location.protocol;
let port = location.port;

@@ -48,8 +40,8 @@ // some user agents have empty `location.port`

if ('open' in xhr && !opts.forceJSONP) {
if ("open" in xhr && !opts.forceJSONP) {
return new XHR(opts);
} else {
if (!jsonp) throw new Error('JSONP disabled');
if (!jsonp) throw new Error("JSONP disabled");
return new JSONP(opts);
}
}

@@ -1,26 +0,11 @@

/**
* Module requirements.
*/
const Polling = require("./polling");
var Polling = require('./polling');
var inherit = require('component-inherit');
const rNewline = /\n/g;
const rEscapedNewline = /\\n/g;
/**
* Module exports.
*/
module.exports = JSONPPolling;
/**
* Cached regular expressions.
*/
var rNewline = /\n/g;
var rEscapedNewline = /\\n/g;
/**
* Global JSONP callbacks.
*/
var callbacks;
let callbacks;

@@ -31,210 +16,200 @@ /**

function empty () { }
function empty() {}
/**
* Until https://github.com/tc39/proposal-global is shipped.
*/
function glob () {
return typeof self !== 'undefined' ? self
: typeof window !== 'undefined' ? window
: typeof global !== 'undefined' ? global : {};
}
class JSONPPolling extends Polling {
/**
* JSONP Polling constructor.
*
* @param {Object} opts.
* @api public
*/
constructor(opts) {
super(opts);
/**
* JSONP Polling constructor.
*
* @param {Object} opts.
* @api public
*/
this.query = this.query || {};
function JSONPPolling (opts) {
Polling.call(this, opts);
// define global callbacks array if not present
// we do this here (lazily) to avoid unneeded global pollution
if (!callbacks) {
// we need to consider multiple engines in the same page
callbacks = global.___eio = global.___eio || [];
}
this.query = this.query || {};
// callback identifier
this.index = callbacks.length;
// define global callbacks array if not present
// we do this here (lazily) to avoid unneeded global pollution
if (!callbacks) {
// we need to consider multiple engines in the same page
var global = glob();
callbacks = global.___eio = (global.___eio || []);
}
// add callback to jsonp global
const self = this;
callbacks.push(function(msg) {
self.onData(msg);
});
// callback identifier
this.index = callbacks.length;
// append to query string
this.query.j = this.index;
// add callback to jsonp global
var self = this;
callbacks.push(function (msg) {
self.onData(msg);
});
// prevent spurious errors from being emitted when the window is unloaded
if (typeof addEventListener === "function") {
addEventListener(
"beforeunload",
function() {
if (self.script) self.script.onerror = empty;
},
false
);
}
}
// append to query string
this.query.j = this.index;
// prevent spurious errors from being emitted when the window is unloaded
if (typeof addEventListener === 'function') {
addEventListener('beforeunload', function () {
if (self.script) self.script.onerror = empty;
}, false);
/**
* JSONP only supports binary as base64 encoded strings
*/
get supportsBinary() {
return false;
}
}
/**
* Inherits from Polling.
*/
/**
* Closes the socket.
*
* @api private
*/
doClose() {
if (this.script) {
this.script.parentNode.removeChild(this.script);
this.script = null;
}
inherit(JSONPPolling, Polling);
if (this.form) {
this.form.parentNode.removeChild(this.form);
this.form = null;
this.iframe = null;
}
/*
* JSONP only supports binary as base64 encoded strings
*/
JSONPPolling.prototype.supportsBinary = false;
/**
* Closes the socket.
*
* @api private
*/
JSONPPolling.prototype.doClose = function () {
if (this.script) {
this.script.parentNode.removeChild(this.script);
this.script = null;
super.doClose();
}
if (this.form) {
this.form.parentNode.removeChild(this.form);
this.form = null;
this.iframe = null;
}
/**
* Starts a poll cycle.
*
* @api private
*/
doPoll() {
const self = this;
const script = document.createElement("script");
Polling.prototype.doClose.call(this);
};
if (this.script) {
this.script.parentNode.removeChild(this.script);
this.script = null;
}
/**
* Starts a poll cycle.
*
* @api private
*/
script.async = true;
script.src = this.uri();
script.onerror = function(e) {
self.onError("jsonp poll error", e);
};
JSONPPolling.prototype.doPoll = function () {
var self = this;
var script = document.createElement('script');
const insertAt = document.getElementsByTagName("script")[0];
if (insertAt) {
insertAt.parentNode.insertBefore(script, insertAt);
} else {
(document.head || document.body).appendChild(script);
}
this.script = script;
if (this.script) {
this.script.parentNode.removeChild(this.script);
this.script = null;
}
const isUAgecko =
"undefined" !== typeof navigator && /gecko/i.test(navigator.userAgent);
script.async = true;
script.src = this.uri();
script.onerror = function (e) {
self.onError('jsonp poll error', e);
};
var insertAt = document.getElementsByTagName('script')[0];
if (insertAt) {
insertAt.parentNode.insertBefore(script, insertAt);
} else {
(document.head || document.body).appendChild(script);
if (isUAgecko) {
setTimeout(function() {
const iframe = document.createElement("iframe");
document.body.appendChild(iframe);
document.body.removeChild(iframe);
}, 100);
}
}
this.script = script;
var isUAgecko = 'undefined' !== typeof navigator && /gecko/i.test(navigator.userAgent);
/**
* Writes with a hidden iframe.
*
* @param {String} data to send
* @param {Function} called upon flush.
* @api private
*/
doWrite(data, fn) {
const self = this;
let iframe;
if (isUAgecko) {
setTimeout(function () {
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
document.body.removeChild(iframe);
}, 100);
}
};
if (!this.form) {
const form = document.createElement("form");
const area = document.createElement("textarea");
const id = (this.iframeId = "eio_iframe_" + this.index);
/**
* Writes with a hidden iframe.
*
* @param {String} data to send
* @param {Function} called upon flush.
* @api private
*/
form.className = "socketio";
form.style.position = "absolute";
form.style.top = "-1000px";
form.style.left = "-1000px";
form.target = id;
form.method = "POST";
form.setAttribute("accept-charset", "utf-8");
area.name = "d";
form.appendChild(area);
document.body.appendChild(form);
JSONPPolling.prototype.doWrite = function (data, fn) {
var self = this;
this.form = form;
this.area = area;
}
if (!this.form) {
var form = document.createElement('form');
var area = document.createElement('textarea');
var id = this.iframeId = 'eio_iframe_' + this.index;
var iframe;
this.form.action = this.uri();
form.className = 'socketio';
form.style.position = 'absolute';
form.style.top = '-1000px';
form.style.left = '-1000px';
form.target = id;
form.method = 'POST';
form.setAttribute('accept-charset', 'utf-8');
area.name = 'd';
form.appendChild(area);
document.body.appendChild(form);
function complete() {
initIframe();
fn();
}
this.form = form;
this.area = area;
}
function initIframe() {
if (self.iframe) {
try {
self.form.removeChild(self.iframe);
} catch (e) {
self.onError("jsonp polling iframe removal error", e);
}
}
this.form.action = this.uri();
function complete () {
initIframe();
fn();
}
function initIframe () {
if (self.iframe) {
try {
self.form.removeChild(self.iframe);
// ie6 dynamic iframes with target="" support (thanks Chris Lambacher)
const html = '<iframe src="javascript:0" name="' + self.iframeId + '">';
iframe = document.createElement(html);
} catch (e) {
self.onError('jsonp polling iframe removal error', e);
iframe = document.createElement("iframe");
iframe.name = self.iframeId;
iframe.src = "javascript:0";
}
}
try {
// ie6 dynamic iframes with target="" support (thanks Chris Lambacher)
var html = '<iframe src="javascript:0" name="' + self.iframeId + '">';
iframe = document.createElement(html);
} catch (e) {
iframe = document.createElement('iframe');
iframe.name = self.iframeId;
iframe.src = 'javascript:0';
iframe.id = self.iframeId;
self.form.appendChild(iframe);
self.iframe = iframe;
}
iframe.id = self.iframeId;
initIframe();
self.form.appendChild(iframe);
self.iframe = iframe;
}
// escape \n to prevent it from being converted into \r\n by some UAs
// double escaping is required for escaped new lines because unescaping of new lines can be done safely on server-side
data = data.replace(rEscapedNewline, "\\\n");
this.area.value = data.replace(rNewline, "\\n");
initIframe();
try {
this.form.submit();
} catch (e) {}
// escape \n to prevent it from being converted into \r\n by some UAs
// double escaping is required for escaped new lines because unescaping of new lines can be done safely on server-side
data = data.replace(rEscapedNewline, '\\\n');
this.area.value = data.replace(rNewline, '\\n');
if (this.iframe.attachEvent) {
this.iframe.onreadystatechange = function() {
if (self.iframe.readyState === "complete") {
complete();
}
};
} else {
this.iframe.onload = complete;
}
}
}
try {
this.form.submit();
} catch (e) {}
if (this.iframe.attachEvent) {
this.iframe.onreadystatechange = function () {
if (self.iframe.readyState === 'complete') {
complete();
}
};
} else {
this.iframe.onload = complete;
}
};
module.exports = JSONPPolling;
/* global attachEvent */
/**
* Module requirements.
*/
const XMLHttpRequest = require("xmlhttprequest-ssl");
const Polling = require("./polling");
const Emitter = require("component-emitter");
const { pick } = require("../util");
var XMLHttpRequest = require('xmlhttprequest-ssl');
var Polling = require('./polling');
var Emitter = require('component-emitter');
var inherit = require('component-inherit');
var debug = require('debug')('engine.io-client:polling-xhr');
const debug = require("debug")("engine.io-client:polling-xhr");
/**
* Module exports.
* Empty function
*/
module.exports = XHR;
module.exports.Request = Request;
function empty() {}
/**
* Empty function
*/
const hasXHR2 = (function() {
const XMLHttpRequest = require("xmlhttprequest-ssl");
const xhr = new XMLHttpRequest({ xdomain: false });
return null != xhr.responseType;
})();
function empty () {}
class XHR extends Polling {
/**
* XHR Polling constructor.
*
* @param {Object} opts
* @api public
*/
constructor(opts) {
super(opts);
/**
* XHR Polling constructor.
*
* @param {Object} opts
* @api public
*/
if (typeof location !== "undefined") {
const isSSL = "https:" === location.protocol;
let port = location.port;
function XHR (opts) {
Polling.call(this, opts);
this.requestTimeout = opts.requestTimeout;
this.extraHeaders = opts.extraHeaders;
// some user agents have empty `location.port`
if (!port) {
port = isSSL ? 443 : 80;
}
if (typeof location !== 'undefined') {
var isSSL = 'https:' === location.protocol;
var port = location.port;
// some user agents have empty `location.port`
if (!port) {
port = isSSL ? 443 : 80;
this.xd =
(typeof location !== "undefined" &&
opts.hostname !== location.hostname) ||
port !== opts.port;
this.xs = opts.secure !== isSSL;
}
/**
* XHR supports binary
*/
const forceBase64 = opts && opts.forceBase64;
this.supportsBinary = hasXHR2 && !forceBase64;
}
this.xd = (typeof location !== 'undefined' && opts.hostname !== location.hostname) ||
port !== opts.port;
this.xs = opts.secure !== isSSL;
/**
* Creates a request.
*
* @param {String} method
* @api private
*/
request(opts = {}) {
Object.assign(
opts,
{ supportsBinary: this.supportsBinary, xd: this.xd, xs: this.xs },
this.opts
);
return new Request(this.uri(), opts);
}
}
/**
* Inherits from Polling.
*/
/**
* Sends data.
*
* @param {String} data to send.
* @param {Function} called upon flush.
* @api private
*/
doWrite(data, fn) {
const isBinary = typeof data !== "string" && data !== undefined;
const req = this.request({
method: "POST",
data: data,
isBinary: isBinary
});
const self = this;
req.on("success", fn);
req.on("error", function(err) {
self.onError("xhr post error", err);
});
}
inherit(XHR, Polling);
/**
* XHR supports binary
*/
XHR.prototype.supportsBinary = true;
/**
* Creates a request.
*
* @param {String} method
* @api private
*/
XHR.prototype.request = function (opts) {
opts = opts || {};
opts.uri = this.uri();
opts.xd = this.xd;
opts.xs = this.xs;
opts.agent = this.agent || false;
opts.supportsBinary = this.supportsBinary;
opts.enablesXDR = this.enablesXDR;
opts.withCredentials = this.withCredentials;
// SSL options for Node.js client
opts.pfx = this.pfx;
opts.key = this.key;
opts.passphrase = this.passphrase;
opts.cert = this.cert;
opts.ca = this.ca;
opts.ciphers = this.ciphers;
opts.rejectUnauthorized = this.rejectUnauthorized;
opts.requestTimeout = this.requestTimeout;
// other options for Node.js client
opts.extraHeaders = this.extraHeaders;
return new Request(opts);
};
/**
* Sends data.
*
* @param {String} data to send.
* @param {Function} called upon flush.
* @api private
*/
XHR.prototype.doWrite = function (data, fn) {
var isBinary = typeof data !== 'string' && data !== undefined;
var req = this.request({ method: 'POST', data: data, isBinary: isBinary });
var self = this;
req.on('success', fn);
req.on('error', function (err) {
self.onError('xhr post error', err);
});
this.sendXhr = req;
};
/**
* Starts a poll cycle.
*
* @api private
*/
XHR.prototype.doPoll = function () {
debug('xhr poll');
var req = this.request();
var self = this;
req.on('data', function (data) {
self.onData(data);
});
req.on('error', function (err) {
self.onError('xhr poll error', err);
});
this.pollXhr = req;
};
/**
* Request constructor
*
* @param {Object} options
* @api public
*/
function Request (opts) {
this.method = opts.method || 'GET';
this.uri = opts.uri;
this.xd = !!opts.xd;
this.xs = !!opts.xs;
this.async = false !== opts.async;
this.data = undefined !== opts.data ? opts.data : null;
this.agent = opts.agent;
this.isBinary = opts.isBinary;
this.supportsBinary = opts.supportsBinary;
this.enablesXDR = opts.enablesXDR;
this.withCredentials = opts.withCredentials;
this.requestTimeout = opts.requestTimeout;
// SSL options for Node.js client
this.pfx = opts.pfx;
this.key = opts.key;
this.passphrase = opts.passphrase;
this.cert = opts.cert;
this.ca = opts.ca;
this.ciphers = opts.ciphers;
this.rejectUnauthorized = opts.rejectUnauthorized;
// other options for Node.js client
this.extraHeaders = opts.extraHeaders;
this.create();
/**
* Starts a poll cycle.
*
* @api private
*/
doPoll() {
debug("xhr poll");
const req = this.request();
const self = this;
req.on("data", function(data) {
self.onData(data);
});
req.on("error", function(err) {
self.onError("xhr poll error", err);
});
}
}
/**
* Mix in `Emitter`.
*/
class Request extends Emitter {
/**
* Request constructor
*
* @param {Object} options
* @api public
*/
constructor(uri, opts) {
super();
this.opts = opts;
Emitter(Request.prototype);
this.method = opts.method || "GET";
this.uri = uri;
this.async = false !== opts.async;
this.data = undefined !== opts.data ? opts.data : null;
this.isBinary = opts.isBinary;
this.supportsBinary = opts.supportsBinary;
/**
* Creates the XHR object and sends the request.
*
* @api private
*/
this.create();
}
Request.prototype.create = function () {
var opts = { agent: this.agent, xdomain: this.xd, xscheme: this.xs, enablesXDR: this.enablesXDR };
/**
* Creates the XHR object and sends the request.
*
* @api private
*/
create() {
const opts = pick(
this.opts,
"agent",
"enablesXDR",
"pfx",
"key",
"passphrase",
"cert",
"ca",
"ciphers",
"rejectUnauthorized"
);
opts.xdomain = !!this.opts.xd;
opts.xscheme = !!this.opts.xs;
// SSL options for Node.js client
opts.pfx = this.pfx;
opts.key = this.key;
opts.passphrase = this.passphrase;
opts.cert = this.cert;
opts.ca = this.ca;
opts.ciphers = this.ciphers;
opts.rejectUnauthorized = this.rejectUnauthorized;
const xhr = (this.xhr = new XMLHttpRequest(opts));
const self = this;
var xhr = this.xhr = new XMLHttpRequest(opts);
var self = this;
try {
debug('xhr open %s: %s', this.method, this.uri);
xhr.open(this.method, this.uri, this.async);
try {
if (this.extraHeaders) {
xhr.setDisableHeaderCheck && xhr.setDisableHeaderCheck(true);
for (var i in this.extraHeaders) {
if (this.extraHeaders.hasOwnProperty(i)) {
xhr.setRequestHeader(i, this.extraHeaders[i]);
debug("xhr open %s: %s", this.method, this.uri);
xhr.open(this.method, this.uri, this.async);
try {
if (this.opts.extraHeaders) {
xhr.setDisableHeaderCheck && xhr.setDisableHeaderCheck(true);
for (let i in this.opts.extraHeaders) {
if (this.opts.extraHeaders.hasOwnProperty(i)) {
xhr.setRequestHeader(i, this.opts.extraHeaders[i]);
}
}
}
} catch (e) {
console.log(e);
}
} catch (e) {}
if ('POST' === this.method) {
if ("POST" === this.method) {
try {
if (this.isBinary) {
xhr.setRequestHeader("Content-type", "application/octet-stream");
} else {
xhr.setRequestHeader("Content-type", "text/plain;charset=UTF-8");
}
} catch (e) {}
}
try {
if (this.isBinary) {
xhr.setRequestHeader('Content-type', 'application/octet-stream');
} else {
xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8');
}
xhr.setRequestHeader("Accept", "*/*");
} catch (e) {}
}
try {
xhr.setRequestHeader('Accept', '*/*');
} catch (e) {}
// ie6 check
if ("withCredentials" in xhr) {
xhr.withCredentials = this.opts.withCredentials;
}
// ie6 check
if ('withCredentials' in xhr) {
xhr.withCredentials = this.withCredentials;
}
if (this.opts.requestTimeout) {
xhr.timeout = this.opts.requestTimeout;
}
if (this.requestTimeout) {
xhr.timeout = this.requestTimeout;
if (this.hasXDR()) {
xhr.onload = function() {
self.onLoad();
};
xhr.onerror = function() {
self.onError(xhr.responseText);
};
} else {
xhr.onreadystatechange = function() {
if (xhr.readyState === 2) {
try {
const contentType = xhr.getResponseHeader("Content-Type");
if (
(self.supportsBinary &&
contentType === "application/octet-stream") ||
contentType === "application/octet-stream; charset=UTF-8"
) {
xhr.responseType = "arraybuffer";
}
} catch (e) {}
}
if (4 !== xhr.readyState) return;
if (200 === xhr.status || 1223 === xhr.status) {
self.onLoad();
} else {
// make sure the `error` event handler that's user-set
// does not throw in the same tick and gets caught here
setTimeout(function() {
self.onError(typeof xhr.status === "number" ? xhr.status : 0);
}, 0);
}
};
}
debug("xhr data %s", this.data);
xhr.send(this.data);
} catch (e) {
// Need to defer since .create() is called directly fhrom the constructor
// and thus the 'error' event can only be only bound *after* this exception
// occurs. Therefore, also, we cannot throw here at all.
setTimeout(function() {
self.onError(e);
}, 0);
return;
}
if (this.hasXDR()) {
xhr.onload = function () {
self.onLoad();
};
xhr.onerror = function () {
self.onError(xhr.responseText);
};
} else {
xhr.onreadystatechange = function () {
if (xhr.readyState === 2) {
try {
var contentType = xhr.getResponseHeader('Content-Type');
if (self.supportsBinary && contentType === 'application/octet-stream' || contentType === 'application/octet-stream; charset=UTF-8') {
xhr.responseType = 'arraybuffer';
}
} catch (e) {}
}
if (4 !== xhr.readyState) return;
if (200 === xhr.status || 1223 === xhr.status) {
self.onLoad();
} else {
// make sure the `error` event handler that's user-set
// does not throw in the same tick and gets caught here
setTimeout(function () {
self.onError(typeof xhr.status === 'number' ? xhr.status : 0);
}, 0);
}
};
if (typeof document !== "undefined") {
this.index = Request.requestsCount++;
Request.requests[this.index] = this;
}
}
debug('xhr data %s', this.data);
xhr.send(this.data);
} catch (e) {
// Need to defer since .create() is called directly fhrom the constructor
// and thus the 'error' event can only be only bound *after* this exception
// occurs. Therefore, also, we cannot throw here at all.
setTimeout(function () {
self.onError(e);
}, 0);
return;
/**
* Called upon successful response.
*
* @api private
*/
onSuccess() {
this.emit("success");
this.cleanup();
}
if (typeof document !== 'undefined') {
this.index = Request.requestsCount++;
Request.requests[this.index] = this;
/**
* Called if we have data.
*
* @api private
*/
onData(data) {
this.emit("data", data);
this.onSuccess();
}
};
/**
* Called upon successful response.
*
* @api private
*/
/**
* Called upon error.
*
* @api private
*/
onError(err) {
this.emit("error", err);
this.cleanup(true);
}
Request.prototype.onSuccess = function () {
this.emit('success');
this.cleanup();
};
/**
* Cleans up house.
*
* @api private
*/
cleanup(fromError) {
if ("undefined" === typeof this.xhr || null === this.xhr) {
return;
}
// xmlhttprequest
if (this.hasXDR()) {
this.xhr.onload = this.xhr.onerror = empty;
} else {
this.xhr.onreadystatechange = empty;
}
/**
* Called if we have data.
*
* @api private
*/
if (fromError) {
try {
this.xhr.abort();
} catch (e) {}
}
Request.prototype.onData = function (data) {
this.emit('data', data);
this.onSuccess();
};
if (typeof document !== "undefined") {
delete Request.requests[this.index];
}
/**
* Called upon error.
*
* @api private
*/
Request.prototype.onError = function (err) {
this.emit('error', err);
this.cleanup(true);
};
/**
* Cleans up house.
*
* @api private
*/
Request.prototype.cleanup = function (fromError) {
if ('undefined' === typeof this.xhr || null === this.xhr) {
return;
this.xhr = null;
}
// xmlhttprequest
if (this.hasXDR()) {
this.xhr.onload = this.xhr.onerror = empty;
} else {
this.xhr.onreadystatechange = empty;
}
if (fromError) {
/**
* Called upon load.
*
* @api private
*/
onLoad() {
let data;
try {
this.xhr.abort();
} catch (e) {}
let contentType;
try {
contentType = this.xhr.getResponseHeader("Content-Type");
} catch (e) {}
if (
contentType === "application/octet-stream" ||
contentType === "application/octet-stream; charset=UTF-8"
) {
data = this.xhr.response || this.xhr.responseText;
} else {
data = this.xhr.responseText;
}
} catch (e) {
this.onError(e);
}
if (null != data) {
this.onData(data);
}
}
if (typeof document !== 'undefined') {
delete Request.requests[this.index];
/**
* Check if it has XDomainRequest.
*
* @api private
*/
hasXDR() {
return typeof XDomainRequest !== "undefined" && !this.xs && this.enablesXDR;
}
this.xhr = null;
};
/**
* Called upon load.
*
* @api private
*/
Request.prototype.onLoad = function () {
var data;
try {
var contentType;
try {
contentType = this.xhr.getResponseHeader('Content-Type');
} catch (e) {}
if (contentType === 'application/octet-stream' || contentType === 'application/octet-stream; charset=UTF-8') {
data = this.xhr.response || this.xhr.responseText;
} else {
data = this.xhr.responseText;
}
} catch (e) {
this.onError(e);
/**
* Aborts the request.
*
* @api public
*/
abort() {
this.cleanup();
}
if (null != data) {
this.onData(data);
}
};
}
/**
* Check if it has XDomainRequest.
*
* @api private
*/
Request.prototype.hasXDR = function () {
return typeof XDomainRequest !== 'undefined' && !this.xs && this.enablesXDR;
};
/**
* Aborts the request.
*
* @api public
*/
Request.prototype.abort = function () {
this.cleanup();
};
/**
* Aborts pending requests when unloading the window. This is needed to prevent

@@ -402,7 +359,7 @@ * memory leaks (e.g. when using IE) and to ensure that no spurious error is

if (typeof document !== 'undefined') {
if (typeof attachEvent === 'function') {
attachEvent('onunload', unloadHandler);
} else if (typeof addEventListener === 'function') {
var terminationEvent = 'onpagehide' in self ? 'pagehide' : 'unload';
if (typeof document !== "undefined") {
if (typeof attachEvent === "function") {
attachEvent("onunload", unloadHandler);
} else if (typeof addEventListener === "function") {
const terminationEvent = "onpagehide" in self ? "pagehide" : "unload";
addEventListener(terminationEvent, unloadHandler, false);

@@ -412,4 +369,4 @@ }

function unloadHandler () {
for (var i in Request.requests) {
function unloadHandler() {
for (let i in Request.requests) {
if (Request.requests.hasOwnProperty(i)) {

@@ -420,1 +377,4 @@ Request.requests[i].abort();

}
module.exports = XHR;
module.exports.Request = Request;

@@ -1,245 +0,212 @@

/**
* Module dependencies.
*/
const Transport = require("../transport");
const parseqs = require("parseqs");
const parser = require("engine.io-parser");
const yeast = require("yeast");
var Transport = require('../transport');
var parseqs = require('parseqs');
var parser = require('engine.io-parser');
var inherit = require('component-inherit');
var yeast = require('yeast');
var debug = require('debug')('engine.io-client:polling');
const debug = require("debug")("engine.io-client:polling");
/**
* Module exports.
*/
class Polling extends Transport {
/**
* Transport name.
*/
get name() {
return "polling";
}
module.exports = Polling;
/**
* Is XHR2 supported?
*/
var hasXHR2 = (function () {
var XMLHttpRequest = require('xmlhttprequest-ssl');
var xhr = new XMLHttpRequest({ xdomain: false });
return null != xhr.responseType;
})();
/**
* Polling interface.
*
* @param {Object} opts
* @api private
*/
function Polling (opts) {
var forceBase64 = (opts && opts.forceBase64);
if (!hasXHR2 || forceBase64) {
this.supportsBinary = false;
/**
* Opens the socket (triggers polling). We write a PING message to determine
* when the transport is open.
*
* @api private
*/
doOpen() {
this.poll();
}
Transport.call(this, opts);
}
/**
* Inherits from Transport.
*/
/**
* Pauses polling.
*
* @param {Function} callback upon buffers are flushed and transport is paused
* @api private
*/
pause(onPause) {
const self = this;
inherit(Polling, Transport);
this.readyState = "pausing";
/**
* Transport name.
*/
function pause() {
debug("paused");
self.readyState = "paused";
onPause();
}
Polling.prototype.name = 'polling';
if (this.polling || !this.writable) {
let total = 0;
/**
* Opens the socket (triggers polling). We write a PING message to determine
* when the transport is open.
*
* @api private
*/
if (this.polling) {
debug("we are currently polling - waiting to pause");
total++;
this.once("pollComplete", function() {
debug("pre-pause polling complete");
--total || pause();
});
}
Polling.prototype.doOpen = function () {
this.poll();
};
if (!this.writable) {
debug("we are currently writing - waiting to pause");
total++;
this.once("drain", function() {
debug("pre-pause writing complete");
--total || pause();
});
}
} else {
pause();
}
}
/**
* Pauses polling.
*
* @param {Function} callback upon buffers are flushed and transport is paused
* @api private
*/
/**
* Starts polling cycle.
*
* @api public
*/
poll() {
debug("polling");
this.polling = true;
this.doPoll();
this.emit("poll");
}
Polling.prototype.pause = function (onPause) {
var self = this;
/**
* Overloads onData to detect payloads.
*
* @api private
*/
onData(data) {
const self = this;
debug("polling got data %s", data);
const callback = function(packet, index, total) {
// if its the first message we consider the transport open
if ("opening" === self.readyState) {
self.onOpen();
}
this.readyState = 'pausing';
// if its a close packet, we close the ongoing requests
if ("close" === packet.type) {
self.onClose();
return false;
}
function pause () {
debug('paused');
self.readyState = 'paused';
onPause();
}
// otherwise bypass onData and handle the message
self.onPacket(packet);
};
if (this.polling || !this.writable) {
var total = 0;
// decode payload
parser.decodePayload(data, this.socket.binaryType, callback);
if (this.polling) {
debug('we are currently polling - waiting to pause');
total++;
this.once('pollComplete', function () {
debug('pre-pause polling complete');
--total || pause();
});
}
// if an event did not trigger closing
if ("closed" !== this.readyState) {
// if we got data we're not polling
this.polling = false;
this.emit("pollComplete");
if (!this.writable) {
debug('we are currently writing - waiting to pause');
total++;
this.once('drain', function () {
debug('pre-pause writing complete');
--total || pause();
});
if ("open" === this.readyState) {
this.poll();
} else {
debug('ignoring poll - transport state "%s"', this.readyState);
}
}
} else {
pause();
}
};
/**
* Starts polling cycle.
*
* @api public
*/
/**
* For polling, send a close packet.
*
* @api private
*/
doClose() {
const self = this;
Polling.prototype.poll = function () {
debug('polling');
this.polling = true;
this.doPoll();
this.emit('poll');
};
/**
* Overloads onData to detect payloads.
*
* @api private
*/
Polling.prototype.onData = function (data) {
var self = this;
debug('polling got data %s', data);
var callback = function (packet, index, total) {
// if its the first message we consider the transport open
if ('opening' === self.readyState) {
self.onOpen();
function close() {
debug("writing close packet");
self.write([{ type: "close" }]);
}
// if its a close packet, we close the ongoing requests
if ('close' === packet.type) {
self.onClose();
return false;
}
// otherwise bypass onData and handle the message
self.onPacket(packet);
};
// decode payload
parser.decodePayload(data, this.socket.binaryType, callback);
// if an event did not trigger closing
if ('closed' !== this.readyState) {
// if we got data we're not polling
this.polling = false;
this.emit('pollComplete');
if ('open' === this.readyState) {
this.poll();
if ("open" === this.readyState) {
debug("transport open - closing");
close();
} else {
debug('ignoring poll - transport state "%s"', this.readyState);
// in case we're trying to close while
// handshaking is in progress (GH-164)
debug("transport not open - deferring close");
this.once("open", close);
}
}
};
/**
* For polling, send a close packet.
*
* @api private
*/
/**
* Writes a packets payload.
*
* @param {Array} data packets
* @param {Function} drain callback
* @api private
*/
write(packets) {
const self = this;
this.writable = false;
const callbackfn = function() {
self.writable = true;
self.emit("drain");
};
Polling.prototype.doClose = function () {
var self = this;
function close () {
debug('writing close packet');
self.write([{ type: 'close' }]);
parser.encodePayload(packets, this.supportsBinary, function(data) {
self.doWrite(data, callbackfn);
});
}
if ('open' === this.readyState) {
debug('transport open - closing');
close();
} else {
// in case we're trying to close while
// handshaking is in progress (GH-164)
debug('transport not open - deferring close');
this.once('open', close);
}
};
/**
* Generates uri for connection.
*
* @api private
*/
uri() {
let query = this.query || {};
const schema = this.opts.secure ? "https" : "http";
let port = "";
/**
* Writes a packets payload.
*
* @param {Array} data packets
* @param {Function} drain callback
* @api private
*/
// cache busting is forced
if (false !== this.opts.timestampRequests) {
query[this.opts.timestampParam] = yeast();
}
Polling.prototype.write = function (packets) {
var self = this;
this.writable = false;
var callbackfn = function () {
self.writable = true;
self.emit('drain');
};
if (!this.supportsBinary && !query.sid) {
query.b64 = 1;
}
parser.encodePayload(packets, this.supportsBinary, function (data) {
self.doWrite(data, callbackfn);
});
};
query = parseqs.encode(query);
/**
* Generates uri for connection.
*
* @api private
*/
// avoid port if default for schema
if (
this.opts.port &&
(("https" === schema && Number(this.opts.port) !== 443) ||
("http" === schema && Number(this.opts.port) !== 80))
) {
port = ":" + this.opts.port;
}
Polling.prototype.uri = function () {
var query = this.query || {};
var schema = this.secure ? 'https' : 'http';
var port = '';
// prepend ? to query
if (query.length) {
query = "?" + query;
}
// cache busting is forced
if (false !== this.timestampRequests) {
query[this.timestampParam] = yeast();
const ipv6 = this.opts.hostname.indexOf(":") !== -1;
return (
schema +
"://" +
(ipv6 ? "[" + this.opts.hostname + "]" : this.opts.hostname) +
port +
this.opts.path +
query
);
}
}
if (!this.supportsBinary && !query.sid) {
query.b64 = 1;
}
query = parseqs.encode(query);
// avoid port if default for schema
if (this.port && (('https' === schema && Number(this.port) !== 443) ||
('http' === schema && Number(this.port) !== 80))) {
port = ':' + this.port;
}
// prepend ? to query
if (query.length) {
query = '?' + query;
}
var ipv6 = this.hostname.indexOf(':') !== -1;
return schema + '://' + (ipv6 ? '[' + this.hostname + ']' : this.hostname) + port + this.path + query;
};
module.exports = Polling;

@@ -1,24 +0,21 @@

/**
* Module dependencies.
*/
const Transport = require("../transport");
const parser = require("engine.io-parser");
const parseqs = require("parseqs");
const yeast = require("yeast");
const { pick } = require("../util");
var Transport = require('../transport');
var parser = require('engine.io-parser');
var parseqs = require('parseqs');
var inherit = require('component-inherit');
var yeast = require('yeast');
var debug = require('debug')('engine.io-client:websocket');
const debug = require("debug")("engine.io-client:websocket");
var BrowserWebSocket, NodeWebSocket;
let BrowserWebSocket, NodeWebSocket;
if (typeof WebSocket !== 'undefined') {
if (typeof WebSocket !== "undefined") {
BrowserWebSocket = WebSocket;
} else if (typeof self !== 'undefined') {
} else if (typeof self !== "undefined") {
BrowserWebSocket = self.WebSocket || self.MozWebSocket;
}
if (typeof window === 'undefined') {
if (typeof window === "undefined") {
try {
NodeWebSocket = require('ws');
} catch (e) { }
NodeWebSocket = require("ws");
} catch (e) {}
}

@@ -32,265 +29,260 @@

var WebSocketImpl = BrowserWebSocket || NodeWebSocket;
let WebSocketImpl = BrowserWebSocket || NodeWebSocket;
/**
* Module exports.
*/
class WS extends Transport {
/**
* WebSocket transport constructor.
*
* @api {Object} connection options
* @api public
*/
constructor(opts) {
super(opts);
module.exports = WS;
const forceBase64 = opts && opts.forceBase64;
if (forceBase64) {
this.supportsBinary = false;
}
this.usingBrowserWebSocket = BrowserWebSocket && !opts.forceNode;
if (!this.usingBrowserWebSocket) {
WebSocketImpl = NodeWebSocket;
}
// WebSockets support binary
this.supportsBinary = true;
}
/**
* WebSocket transport constructor.
*
* @api {Object} connection options
* @api public
*/
function WS (opts) {
var forceBase64 = (opts && opts.forceBase64);
if (forceBase64) {
this.supportsBinary = false;
/**
* Transport name.
*
* @api public
*/
get name() {
return "websocket";
}
this.perMessageDeflate = opts.perMessageDeflate;
this.usingBrowserWebSocket = BrowserWebSocket && !opts.forceNode;
this.protocols = opts.protocols;
if (!this.usingBrowserWebSocket) {
WebSocketImpl = NodeWebSocket;
}
Transport.call(this, opts);
}
/**
* Inherits from Transport.
*/
/**
* Opens socket.
*
* @api private
*/
doOpen() {
if (!this.check()) {
// let probe timeout
return;
}
inherit(WS, Transport);
const uri = this.uri();
const protocols = this.opts.protocols;
const opts = pick(
this.opts,
"agent",
"perMessageDeflate",
"pfx",
"key",
"passphrase",
"cert",
"ca",
"ciphers",
"rejectUnauthorized",
"localAddress"
);
if (this.opts.extraHeaders) {
opts.headers = this.opts.extraHeaders;
}
/**
* Transport name.
*
* @api public
*/
try {
this.ws =
this.usingBrowserWebSocket && !this.opts.isReactNative
? protocols
? new WebSocketImpl(uri, protocols)
: new WebSocketImpl(uri)
: new WebSocketImpl(uri, protocols, opts);
} catch (err) {
return this.emit("error", err);
}
WS.prototype.name = 'websocket';
if (this.ws.binaryType === undefined) {
this.supportsBinary = false;
}
/*
* WebSockets support binary
*/
if (this.ws.supports && this.ws.supports.binary) {
this.supportsBinary = true;
this.ws.binaryType = "nodebuffer";
} else {
this.ws.binaryType = "arraybuffer";
}
WS.prototype.supportsBinary = true;
/**
* Opens socket.
*
* @api private
*/
WS.prototype.doOpen = function () {
if (!this.check()) {
// let probe timeout
return;
this.addEventListeners();
}
var uri = this.uri();
var protocols = this.protocols;
var opts = {
agent: this.agent,
perMessageDeflate: this.perMessageDeflate
};
/**
* Adds event listeners to the socket
*
* @api private
*/
addEventListeners() {
const self = this;
// SSL options for Node.js client
opts.pfx = this.pfx;
opts.key = this.key;
opts.passphrase = this.passphrase;
opts.cert = this.cert;
opts.ca = this.ca;
opts.ciphers = this.ciphers;
opts.rejectUnauthorized = this.rejectUnauthorized;
if (this.extraHeaders) {
opts.headers = this.extraHeaders;
this.ws.onopen = function() {
self.onOpen();
};
this.ws.onclose = function() {
self.onClose();
};
this.ws.onmessage = function(ev) {
self.onData(ev.data);
};
this.ws.onerror = function(e) {
self.onError("websocket error", e);
};
}
if (this.localAddress) {
opts.localAddress = this.localAddress;
}
try {
this.ws =
this.usingBrowserWebSocket && !this.isReactNative
? protocols
? new WebSocketImpl(uri, protocols)
: new WebSocketImpl(uri)
: new WebSocketImpl(uri, protocols, opts);
} catch (err) {
return this.emit('error', err);
}
/**
* Writes data to socket.
*
* @param {Array} array of packets.
* @api private
*/
write(packets) {
const self = this;
this.writable = false;
if (this.ws.binaryType === undefined) {
this.supportsBinary = false;
}
if (this.ws.supports && this.ws.supports.binary) {
this.supportsBinary = true;
this.ws.binaryType = 'nodebuffer';
} else {
this.ws.binaryType = 'arraybuffer';
}
this.addEventListeners();
};
/**
* Adds event listeners to the socket
*
* @api private
*/
WS.prototype.addEventListeners = function () {
var self = this;
this.ws.onopen = function () {
self.onOpen();
};
this.ws.onclose = function () {
self.onClose();
};
this.ws.onmessage = function (ev) {
self.onData(ev.data);
};
this.ws.onerror = function (e) {
self.onError('websocket error', e);
};
};
/**
* Writes data to socket.
*
* @param {Array} array of packets.
* @api private
*/
WS.prototype.write = function (packets) {
var self = this;
this.writable = false;
// encodePacket efficient as it uses WS framing
// no need for encodePayload
var total = packets.length;
for (var i = 0, l = total; i < l; i++) {
(function (packet) {
parser.encodePacket(packet, self.supportsBinary, function (data) {
if (!self.usingBrowserWebSocket) {
// encodePacket efficient as it uses WS framing
// no need for encodePayload
let total = packets.length;
let i = 0;
const l = total;
for (; i < l; i++) {
(function(packet) {
parser.encodePacket(packet, self.supportsBinary, function(data) {
// always create a new object (GH-437)
var opts = {};
if (packet.options) {
opts.compress = packet.options.compress;
}
const opts = {};
if (!self.usingBrowserWebSocket) {
if (packet.options) {
opts.compress = packet.options.compress;
}
if (self.perMessageDeflate) {
var len = 'string' === typeof data ? Buffer.byteLength(data) : data.length;
if (len < self.perMessageDeflate.threshold) {
opts.compress = false;
if (self.opts.perMessageDeflate) {
const len =
"string" === typeof data
? Buffer.byteLength(data)
: data.length;
if (len < self.opts.perMessageDeflate.threshold) {
opts.compress = false;
}
}
}
}
// Sometimes the websocket has already been closed but the browser didn't
// have a chance of informing us about it yet, in that case send will
// throw an error
try {
if (self.usingBrowserWebSocket) {
// TypeError is thrown when passing the second argument on Safari
self.ws.send(data);
} else {
self.ws.send(data, opts);
// Sometimes the websocket has already been closed but the browser didn't
// have a chance of informing us about it yet, in that case send will
// throw an error
try {
if (self.usingBrowserWebSocket) {
// TypeError is thrown when passing the second argument on Safari
self.ws.send(data);
} else {
self.ws.send(data, opts);
}
} catch (e) {
debug("websocket closed before onclose event");
}
} catch (e) {
debug('websocket closed before onclose event');
}
--total || done();
});
})(packets[i]);
}
--total || done();
});
})(packets[i]);
}
function done () {
self.emit('flush');
function done() {
self.emit("flush");
// fake drain
// defer to next tick to allow Socket to clear writeBuffer
setTimeout(function () {
self.writable = true;
self.emit('drain');
}, 0);
// fake drain
// defer to next tick to allow Socket to clear writeBuffer
setTimeout(function() {
self.writable = true;
self.emit("drain");
}, 0);
}
}
};
/**
* Called upon close
*
* @api private
*/
/**
* Called upon close
*
* @api private
*/
onClose() {
Transport.prototype.onClose.call(this);
}
WS.prototype.onClose = function () {
Transport.prototype.onClose.call(this);
};
/**
* Closes socket.
*
* @api private
*/
doClose() {
if (typeof this.ws !== "undefined") {
this.ws.close();
}
}
/**
* Closes socket.
*
* @api private
*/
/**
* Generates uri for connection.
*
* @api private
*/
uri() {
let query = this.query || {};
const schema = this.opts.secure ? "wss" : "ws";
let port = "";
WS.prototype.doClose = function () {
if (typeof this.ws !== 'undefined') {
this.ws.close();
}
};
// avoid port if default for schema
if (
this.opts.port &&
(("wss" === schema && Number(this.opts.port) !== 443) ||
("ws" === schema && Number(this.opts.port) !== 80))
) {
port = ":" + this.opts.port;
}
/**
* Generates uri for connection.
*
* @api private
*/
// append timestamp to URI
if (this.opts.timestampRequests) {
query[this.opts.timestampParam] = yeast();
}
WS.prototype.uri = function () {
var query = this.query || {};
var schema = this.secure ? 'wss' : 'ws';
var port = '';
// communicate binary support capabilities
if (!this.supportsBinary) {
query.b64 = 1;
}
// avoid port if default for schema
if (this.port && (('wss' === schema && Number(this.port) !== 443) ||
('ws' === schema && Number(this.port) !== 80))) {
port = ':' + this.port;
}
query = parseqs.encode(query);
// append timestamp to URI
if (this.timestampRequests) {
query[this.timestampParam] = yeast();
}
// prepend ? to query
if (query.length) {
query = "?" + query;
}
// communicate binary support capabilities
if (!this.supportsBinary) {
query.b64 = 1;
const ipv6 = this.opts.hostname.indexOf(":") !== -1;
return (
schema +
"://" +
(ipv6 ? "[" + this.opts.hostname + "]" : this.opts.hostname) +
port +
this.opts.path +
query
);
}
query = parseqs.encode(query);
// prepend ? to query
if (query.length) {
query = '?' + query;
/**
* Feature detection for WebSocket.
*
* @return {Boolean} whether this transport is available.
* @api public
*/
check() {
return (
!!WebSocketImpl &&
!("__initialize" in WebSocketImpl && this.name === WS.prototype.name)
);
}
}
var ipv6 = this.hostname.indexOf(':') !== -1;
return schema + '://' + (ipv6 ? '[' + this.hostname + ']' : this.hostname) + port + this.path + query;
};
/**
* Feature detection for WebSocket.
*
* @return {Boolean} whether this transport is available.
* @api public
*/
WS.prototype.check = function () {
return !!WebSocketImpl && !('__initialize' in WebSocketImpl && this.name === WS.prototype.name);
};
module.exports = WS;
// browser shim for xmlhttprequest module
var hasCORS = require('has-cors');
const hasCORS = require("has-cors");
module.exports = function (opts) {
var xdomain = opts.xdomain;
module.exports = function(opts) {
const xdomain = opts.xdomain;
// scheme must be same when usign XDomainRequest
// http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx
var xscheme = opts.xscheme;
const xscheme = opts.xscheme;
// XDomainRequest has a flow of not sending cookie, therefore it should be disabled as a default.
// https://github.com/Automattic/engine.io-client/pull/217
var enablesXDR = opts.enablesXDR;
const enablesXDR = opts.enablesXDR;
// XMLHttpRequest can be disabled on IE
try {
if ('undefined' !== typeof XMLHttpRequest && (!xdomain || hasCORS)) {
if ("undefined" !== typeof XMLHttpRequest && (!xdomain || hasCORS)) {
return new XMLHttpRequest();
}
} catch (e) { }
} catch (e) {}

@@ -27,12 +27,14 @@ // Use XDomainRequest for IE8 if enablesXDR is true

try {
if ('undefined' !== typeof XDomainRequest && !xscheme && enablesXDR) {
if ("undefined" !== typeof XDomainRequest && !xscheme && enablesXDR) {
return new XDomainRequest();
}
} catch (e) { }
} catch (e) {}
if (!xdomain) {
try {
return new self[['Active'].concat('Object').join('X')]('Microsoft.XMLHTTP');
} catch (e) { }
return new self[["Active"].concat("Object").join("X")](
"Microsoft.XMLHTTP"
);
} catch (e) {}
}
};

@@ -5,3 +5,3 @@ {

"license": "MIT",
"version": "3.4.0",
"version": "4.0.0-alpha.0",
"main": "lib/index.js",

@@ -29,5 +29,4 @@ "homepage": "https://github.com/socketio/engine.io-client",

"component-emitter": "1.2.1",
"component-inherit": "0.0.3",
"debug": "~4.1.0",
"engine.io-parser": "~2.2.0",
"engine.io-parser": "~4.0.0-alpha.0",
"has-cors": "1.1.0",

@@ -37,3 +36,3 @@ "indexof": "0.0.1",

"parseuri": "0.0.5",
"ws": "~6.1.0",
"ws": "~7.2.1",
"xmlhttprequest-ssl": "~1.5.4",

@@ -43,25 +42,17 @@ "yeast": "0.1.2"

"devDependencies": {
"babel-core": "^6.24.0",
"babel-eslint": "4.1.7",
"babel-loader": "^6.4.1",
"babel-preset-es2015": "^6.24.0",
"@babel/core": "^7.7.7",
"@babel/plugin-transform-object-assign": "~7.8.3",
"@babel/preset-env": "^7.7.7",
"babel-eslint": "^10.0.3",
"babel-loader": "^8.0.6",
"blob": "^0.0.4",
"concat-stream": "^1.6.0",
"del": "^2.2.2",
"derequire": "^2.0.6",
"engine.io": "3.4.0",
"eslint-config-standard": "4.4.0",
"eslint-plugin-standard": "1.3.1",
"engine.io": "git+https://github.com/socketio/engine.io.git#v4",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.9.0",
"expect.js": "^0.3.1",
"express": "4.15.2",
"gulp": "3.9.1",
"gulp-eslint": "1.1.1",
"gulp-file": "^0.3.0",
"gulp-istanbul": "^1.1.1",
"gulp-mocha": "^4.3.0",
"gulp-task-listing": "1.0.1",
"istanbul": "^0.4.5",
"mocha": "^3.2.0",
"webpack": "1.12.12",
"webpack-stream": "^3.2.0",
"prettier": "^1.19.1",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10",
"zuul": "3.11.1",

@@ -72,3 +63,9 @@ "zuul-builder-webpack": "^1.2.0",

"scripts": {
"test": "gulp test"
"test": "npm run lint && npm run format:check && if test \"$BROWSERS\" = \"1\" ; then npm run test:browser; else npm run test:node; fi",
"test:node": "mocha --reporter dot --require test/support/server.js test/index.js",
"test:browser": "zuul test/index.js",
"build": "webpack --config ./support/webpack.config.js",
"format:check": "prettier --check 'lib/**/*.js' 'test/**/*.js'",
"format:fix": "prettier --write 'lib/**/*.js' 'test/**/*.js'",
"lint": "eslint lib/**/*.js test/**/*.js"
},

@@ -86,4 +83,4 @@ "browser": {

"lib/",
"engine.io.js"
"dist/"
]
}

@@ -175,5 +175,5 @@

- `ping`
- Fired upon _flushing_ a ping packet (ie: actual packet write out)
- Fired upon receiving a ping packet.
- `pong`
- Fired upon receiving a pong packet.
- Fired upon _flushing_ a pong packet (ie: actual packet write out)

@@ -180,0 +180,0 @@ #### Methods

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