Socket
Socket
Sign inDemoInstall

engine.io

Package Overview
Dependencies
Maintainers
2
Versions
151
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

engine.io - npm Package Compare versions

Comparing version 5.0.0 to 5.1.0

15

CHANGELOG.md

@@ -0,1 +1,16 @@

# [5.1.0](https://github.com/socketio/engine.io/compare/5.0.0...5.1.0) (2021-05-04)
### Features
* add a "connection_error" event ([7096e98](https://github.com/socketio/engine.io/commit/7096e98a02295a62c8ea2aa56461d4875887092d))
* add the "initial_headers" and "headers" events ([2527543](https://github.com/socketio/engine.io/commit/252754353a0e88eb036ebb3082e9d6a9a5f497db))
### Performance Improvements
* **websocket:** add a "wsPreEncoded" writing option ([7706b12](https://github.com/socketio/engine.io/commit/7706b123df914777d19c8179b45ab6932f82916c))
* **websocket:** fix write back-pressure ([#618](https://github.com/socketio/engine.io/issues/618)) ([ad5306a](https://github.com/socketio/engine.io/commit/ad5306aeaedf06ac7a49f791e1b76e55c35a564e))
# [5.0.0](https://github.com/socketio/engine.io/compare/4.1.1...5.0.0) (2021-03-10)

@@ -2,0 +17,0 @@

217

lib/server.js

@@ -87,2 +87,21 @@ const qs = require("querystring");

});
if (typeof this.ws.on === "function") {
this.ws.on("headers", (headersArray, req) => {
// note: 'ws' uses an array of headers, while Engine.IO uses an object (response.writeHead() accepts both formats)
// we could also try to parse the array and then sync the values, but that will be error-prone
const additionalHeaders = {};
const isInitialRequest = !req._query.sid;
if (isInitialRequest) {
this.emit("initial_headers", additionalHeaders, req);
}
this.emit("headers", additionalHeaders, req);
Object.keys(additionalHeaders).forEach(key => {
headersArray.push(`${key}: ${additionalHeaders[key]}`);
});
});
}
}

@@ -113,3 +132,3 @@

debug('unknown transport "%s"', transport);
return fn(Server.errors.UNKNOWN_TRANSPORT, false);
return fn(Server.errors.UNKNOWN_TRANSPORT, { transport });
}

@@ -120,5 +139,9 @@

if (isOriginInvalid) {
const origin = req.headers.origin;
req.headers.origin = null;
debug("origin header invalid");
return fn(Server.errors.BAD_REQUEST, false);
return fn(Server.errors.BAD_REQUEST, {
name: "INVALID_ORIGIN",
origin
});
}

@@ -131,17 +154,36 @@

debug('unknown sid "%s"', sid);
return fn(Server.errors.UNKNOWN_SID, false);
return fn(Server.errors.UNKNOWN_SID, {
sid
});
}
if (!upgrade && this.clients[sid].transport.name !== transport) {
const previousTransport = this.clients[sid].transport.name;
if (!upgrade && previousTransport !== transport) {
debug("bad request: unexpected transport without upgrade");
return fn(Server.errors.BAD_REQUEST, false);
return fn(Server.errors.BAD_REQUEST, {
name: "TRANSPORT_MISMATCH",
transport,
previousTransport
});
}
} else {
// handshake is GET only
if ("GET" !== req.method)
return fn(Server.errors.BAD_HANDSHAKE_METHOD, false);
if (!this.opts.allowRequest) return fn(null, true);
return this.opts.allowRequest(req, fn);
if ("GET" !== req.method) {
return fn(Server.errors.BAD_HANDSHAKE_METHOD, {
method: req.method
});
}
if (!this.opts.allowRequest) return fn();
return this.opts.allowRequest(req, (message, success) => {
if (!success) {
return fn(Server.errors.FORBIDDEN, {
message
});
}
fn();
});
}
fn(null, true);
fn();
}

@@ -193,5 +235,11 @@

const callback = (err, success) => {
if (!success) {
sendErrorMessage(req, res, err);
const callback = (errorCode, errorContext) => {
if (errorCode !== undefined) {
this.emit("connection_error", {
req,
code: errorCode,
message: Server.errorMessages[errorCode],
context: errorContext
});
sendErrorMessage(req, res, errorCode, errorContext);
return;

@@ -239,2 +287,11 @@ }

debug("unsupported protocol version");
this.emit("connection_error", {
req,
code: Server.errors.UNSUPPORTED_PROTOCOL_VERSION,
message:
Server.errorMessages[Server.errors.UNSUPPORTED_PROTOCOL_VERSION],
context: {
protocol
}
});
sendErrorMessage(

@@ -253,2 +310,11 @@ req,

debug("error while generating an id");
this.emit("connection_error", {
req,
code: Server.errors.BAD_REQUEST,
message: Server.errorMessages[Server.errors.BAD_REQUEST],
context: {
name: "ID_GENERATION_ERROR",
error: e
}
});
sendErrorMessage(req, req.res, Server.errors.BAD_REQUEST);

@@ -276,2 +342,11 @@ return;

debug('error handshaking to transport "%s"', transportName);
this.emit("connection_error", {
req,
code: Server.errors.BAD_REQUEST,
message: Server.errorMessages[Server.errors.BAD_REQUEST],
context: {
name: "TRANSPORT_HANDSHAKE_ERROR",
error: e
}
});
sendErrorMessage(req, req.res, Server.errors.BAD_REQUEST);

@@ -281,14 +356,17 @@ return;

const socket = new Socket(id, this, transport, req, protocol);
const self = this;
if (this.opts.cookie) {
transport.on("headers", headers => {
headers["Set-Cookie"] = cookieMod.serialize(
this.opts.cookie.name,
id,
this.opts.cookie
);
});
}
transport.on("headers", (headers, req) => {
const isInitialRequest = !req._query.sid;
if (isInitialRequest) {
if (this.opts.cookie) {
headers["Set-Cookie"] = [
cookieMod.serialize(this.opts.cookie.name, id, this.opts.cookie)
];
}
this.emit("initial_headers", headers, req);
}
this.emit("headers", headers, req);
});
transport.onRequest(req);

@@ -299,5 +377,5 @@

socket.once("close", function() {
delete self.clients[id];
self.clientsCount--;
socket.once("close", () => {
delete this.clients[id];
this.clientsCount--;
});

@@ -316,6 +394,11 @@

const self = this;
this.verify(req, true, function(err, success) {
if (!success) {
abortConnection(socket, err);
this.verify(req, true, (errorCode, errorContext) => {
if (errorCode) {
this.emit("connection_error", {
req,
code: errorCode,
message: Server.errorMessages[errorCode],
context: errorContext
});
abortConnection(socket, errorCode, errorContext);
return;

@@ -328,4 +411,4 @@ }

// delegate to ws
self.ws.handleUpgrade(req, socket, head, function(conn) {
self.onWebSocket(req, conn);
this.ws.handleUpgrade(req, socket, head, conn => {
this.onWebSocket(req, conn);
});

@@ -405,5 +488,3 @@ });

*/
attach(server, options) {
const self = this;
options = options || {};
attach(server, options = {}) {
let path = (options.path || "/engine.io").replace(/\/$/, "");

@@ -423,10 +504,10 @@

server.removeAllListeners("request");
server.on("close", self.close.bind(self));
server.on("listening", self.init.bind(self));
server.on("close", this.close.bind(this));
server.on("listening", this.init.bind(this));
// add request handler
server.on("request", function(req, res) {
server.on("request", (req, res) => {
if (check(req)) {
debug('intercepting request for path "%s"', path);
self.handleRequest(req, res);
this.handleRequest(req, res);
} else {

@@ -441,6 +522,6 @@ let i = 0;

if (~self.opts.transports.indexOf("websocket")) {
server.on("upgrade", function(req, socket, head) {
if (~this.opts.transports.indexOf("websocket")) {
server.on("upgrade", (req, socket, head) => {
if (check(req)) {
self.handleUpgrade(req, socket, head);
this.handleUpgrade(req, socket, head);
} else if (false !== options.destroyUpgrade) {

@@ -487,30 +568,24 @@ // default node behavior is to disconnect when no handlers

*
* @param {http.ServerResponse} response
* @param {code} error code
* @param req - the request object
* @param res - the response object
* @param errorCode - the error code
* @param errorContext - additional error context
*
* @api private
*/
function sendErrorMessage(req, res, code) {
const headers = { "Content-Type": "application/json" };
function sendErrorMessage(req, res, errorCode, errorContext) {
const statusCode = errorCode === Server.errors.FORBIDDEN ? 403 : 400;
const message =
errorContext && errorContext.message
? errorContext.message
: Server.errorMessages[errorCode];
const isForbidden = !Server.errorMessages.hasOwnProperty(code);
if (isForbidden) {
res.writeHead(403, headers);
res.end(
JSON.stringify({
code: Server.errors.FORBIDDEN,
message: code || Server.errorMessages[Server.errors.FORBIDDEN]
})
);
return;
}
if (res !== undefined) {
res.writeHead(400, headers);
res.end(
JSON.stringify({
code: code,
message: Server.errorMessages[code]
})
);
}
res.writeHead(statusCode, { "Content-Type": "application/json" });
res.end(
JSON.stringify({
code: errorCode,
message
})
);
}

@@ -522,7 +597,9 @@

* @param {net.Socket} socket
* @param {code} error code
* @param {string} errorCode - the error code
* @param {object} errorContext - additional error context
*
* @api private
*/
function abortConnection(socket, code) {
function abortConnection(socket, errorCode, errorContext) {
socket.on("error", () => {

@@ -532,5 +609,3 @@ debug("ignoring error from closed connection");

if (socket.writable) {
const message = Server.errorMessages.hasOwnProperty(code)
? Server.errorMessages[code]
: String(code || "");
const message = errorContext.message || Server.errorMessages[errorCode];
const length = Buffer.byteLength(message);

@@ -537,0 +612,0 @@ socket.write(

@@ -84,45 +84,44 @@ const EventEmitter = require("events");

onPacket(packet) {
if ("open" === this.readyState) {
// export packet event
debug("packet");
this.emit("packet", packet);
if ("open" !== this.readyState) {
return debug("packet received with closed socket");
}
// export packet event
debug(`received packet ${packet.type}`);
this.emit("packet", packet);
// Reset ping timeout on any packet, incoming data is a good sign of
// other side's liveness
this.resetPingTimeout(
this.server.opts.pingInterval + this.server.opts.pingTimeout
);
// Reset ping timeout on any packet, incoming data is a good sign of
// other side's liveness
this.resetPingTimeout(
this.server.opts.pingInterval + this.server.opts.pingTimeout
);
switch (packet.type) {
case "ping":
if (this.transport.protocol !== 3) {
this.onError("invalid heartbeat direction");
return;
}
debug("got ping");
this.sendPacket("pong");
this.emit("heartbeat");
break;
switch (packet.type) {
case "ping":
if (this.transport.protocol !== 3) {
this.onError("invalid heartbeat direction");
return;
}
debug("got ping");
this.sendPacket("pong");
this.emit("heartbeat");
break;
case "pong":
if (this.transport.protocol === 3) {
this.onError("invalid heartbeat direction");
return;
}
debug("got pong");
this.schedulePing();
this.emit("heartbeat");
break;
case "pong":
if (this.transport.protocol === 3) {
this.onError("invalid heartbeat direction");
return;
}
debug("got pong");
this.schedulePing();
this.emit("heartbeat");
break;
case "error":
this.onClose("parse error");
break;
case "error":
this.onClose("parse error");
break;
case "message":
this.emit("data", packet.data);
this.emit("message", packet.data);
break;
}
} else {
debug("packet received with closed socket");
case "message":
this.emit("data", packet.data);
this.emit("message", packet.data);
break;
}

@@ -216,6 +215,4 @@ }

const self = this;
// set transport upgrade timer
self.upgradeTimeoutTimer = setTimeout(function() {
this.upgradeTimeoutTimer = setTimeout(() => {
debug("client did not complete upgrade - closing transport");

@@ -228,20 +225,20 @@ cleanup();

function onPacket(packet) {
const onPacket = packet => {
if ("ping" === packet.type && "probe" === packet.data) {
transport.send([{ type: "pong", data: "probe" }]);
self.emit("upgrading", transport);
clearInterval(self.checkIntervalTimer);
self.checkIntervalTimer = setInterval(check, 100);
} else if ("upgrade" === packet.type && self.readyState !== "closed") {
this.emit("upgrading", transport);
clearInterval(this.checkIntervalTimer);
this.checkIntervalTimer = setInterval(check, 100);
} else if ("upgrade" === packet.type && this.readyState !== "closed") {
debug("got upgrade packet - upgrading");
cleanup();
self.transport.discard();
self.upgraded = true;
self.clearTransport();
self.setTransport(transport);
self.emit("upgrade", transport);
self.flush();
if (self.readyState === "closing") {
transport.close(function() {
self.onClose("forced close");
this.transport.discard();
this.upgraded = true;
this.clearTransport();
this.setTransport(transport);
this.emit("upgrade", transport);
this.flush();
if (this.readyState === "closing") {
transport.close(() => {
this.onClose("forced close");
});

@@ -253,20 +250,20 @@ }

}
}
};
// we force a polling cycle to ensure a fast upgrade
function check() {
if ("polling" === self.transport.name && self.transport.writable) {
const check = () => {
if ("polling" === this.transport.name && this.transport.writable) {
debug("writing a noop packet to polling for fast upgrade");
self.transport.send([{ type: "noop" }]);
this.transport.send([{ type: "noop" }]);
}
}
};
function cleanup() {
self.upgrading = false;
const cleanup = () => {
this.upgrading = false;
clearInterval(self.checkIntervalTimer);
self.checkIntervalTimer = null;
clearInterval(this.checkIntervalTimer);
this.checkIntervalTimer = null;
clearTimeout(self.upgradeTimeoutTimer);
self.upgradeTimeoutTimer = null;
clearTimeout(this.upgradeTimeoutTimer);
this.upgradeTimeoutTimer = null;

@@ -276,6 +273,6 @@ transport.removeListener("packet", onPacket);

transport.removeListener("error", onError);
self.removeListener("close", onClose);
}
this.removeListener("close", onClose);
};
function onError(err) {
const onError = err => {
debug("client did not complete upgrade - %s", err);

@@ -285,11 +282,11 @@ cleanup();

transport = null;
}
};
function onTransportClose() {
const onTransportClose = () => {
onError("transport closed");
}
};
function onClose() {
const onClose = () => {
onError("socket closed");
}
};

@@ -300,3 +297,3 @@ transport.on("packet", onPacket);

self.once("close", onClose);
this.once("close", onClose);
}

@@ -346,7 +343,6 @@

clearTimeout(this.upgradeTimeoutTimer);
const self = this;
// clean writeBuffer in next tick, so developers can still
// grab the writeBuffer on 'close' event
process.nextTick(function() {
self.writeBuffer = [];
process.nextTick(() => {
this.writeBuffer = [];
});

@@ -366,16 +362,9 @@ this.packetsFn = [];

setupSendCallback() {
const self = this;
this.transport.on("drain", onDrain);
this.cleanupFn.push(function() {
self.transport.removeListener("drain", onDrain);
});
// the message was sent successfully, execute the callback
function onDrain() {
if (self.sentCallbackFn.length > 0) {
const seqFn = self.sentCallbackFn.splice(0, 1)[0];
const onDrain = () => {
if (this.sentCallbackFn.length > 0) {
const seqFn = this.sentCallbackFn.splice(0, 1)[0];
if ("function" === typeof seqFn) {
debug("executing send callback");
seqFn(self.transport);
seqFn(this.transport);
} else if (Array.isArray(seqFn)) {

@@ -387,3 +376,3 @@ debug("executing batch send callback");

if ("function" === typeof seqFn[i]) {
seqFn[i](self.transport);
seqFn[i](this.transport);
}

@@ -393,3 +382,9 @@ }

}
}
};
this.transport.on("drain", onDrain);
this.cleanupFn.push(() => {
this.transport.removeListener("drain", onDrain);
});
}

@@ -396,0 +391,0 @@

@@ -73,12 +73,10 @@ const Transport = require("../transport");

const self = this;
const onClose = () => {
this.onError("poll connection closed prematurely");
};
function onClose() {
self.onError("poll connection closed prematurely");
}
function cleanup() {
const cleanup = () => {
req.removeListener("close", onClose);
self.req = self.res = null;
}
this.req = this.res = null;
};

@@ -122,17 +120,16 @@ req.cleanup = cleanup;

let chunks = isBinary ? Buffer.concat([]) : "";
const self = this;
function cleanup() {
const cleanup = () => {
req.removeListener("data", onData);
req.removeListener("end", onEnd);
req.removeListener("close", onClose);
self.dataReq = self.dataRes = chunks = null;
}
this.dataReq = this.dataRes = chunks = null;
};
function onClose() {
const onClose = () => {
cleanup();
self.onError("data request connection closed prematurely");
}
this.onError("data request connection closed prematurely");
};
function onData(data) {
const onData = data => {
let contentLength;

@@ -147,10 +144,10 @@ if (isBinary) {

if (contentLength > self.maxHttpBufferSize) {
if (contentLength > this.maxHttpBufferSize) {
chunks = isBinary ? Buffer.concat([]) : "";
req.connection.destroy();
}
}
};
function onEnd() {
self.onData(chunks);
const onEnd = () => {
this.onData(chunks);

@@ -164,6 +161,6 @@ const headers = {

res.writeHead(200, self.headers(req, headers));
res.writeHead(200, this.headers(req, headers));
res.end("ok");
cleanup();
}
};

@@ -184,11 +181,10 @@ req.on("close", onClose);

debug('received "%s"', data);
const self = this;
const callback = function(packet) {
const callback = packet => {
if ("close" === packet.type) {
debug("got xhr close packet");
self.onClose();
this.onClose();
return false;
}
self.onPacket(packet);
this.onPacket(packet);
};

@@ -255,5 +251,4 @@

debug('writing "%s"', data);
const self = this;
this.doWrite(data, options, function() {
self.req.cleanup();
this.doWrite(data, options, () => {
this.req.cleanup();
});

@@ -268,4 +263,2 @@ }

doWrite(data, options, callback) {
const self = this;
// explicit UTF-8 is required for pages not served under utf

@@ -281,2 +274,10 @@ const isString = typeof data === "string";

const respond = data => {
headers["Content-Length"] =
"string" === typeof data ? Buffer.byteLength(data) : data.length;
this.res.writeHead(200, this.headers(this.req, headers));
this.res.end(data);
callback();
};
if (!this.httpCompression || !options.compress) {

@@ -299,6 +300,6 @@ respond(data);

this.compress(data, encoding, function(err, data) {
this.compress(data, encoding, (err, data) => {
if (err) {
self.res.writeHead(500);
self.res.end();
this.res.writeHead(500);
this.res.end();
callback(err);

@@ -311,10 +312,2 @@ return;

});
function respond(data) {
headers["Content-Length"] =
"string" === typeof data ? Buffer.byteLength(data) : data.length;
self.res.writeHead(200, self.headers(self.req, headers));
self.res.end(data);
callback();
}
}

@@ -353,3 +346,2 @@

const self = this;
let closeTimeoutTimer;

@@ -362,2 +354,8 @@

const onClose = () => {
clearTimeout(closeTimeoutTimer);
fn();
this.onClose();
};
if (this.writable) {

@@ -375,8 +373,2 @@ debug("transport writable - closing right away");

}
function onClose() {
clearTimeout(closeTimeoutTimer);
fn();
self.onClose();
}
}

@@ -401,3 +393,3 @@

this.emit("headers", headers);
this.emit("headers", headers, req);
return headers;

@@ -404,0 +396,0 @@ }

@@ -17,5 +17,2 @@ const Transport = require("../transport");

this.socket.on("error", this.onError.bind(this));
this.socket.on("headers", headers => {
this.emit("headers", headers);
});
this.writable = true;

@@ -70,34 +67,36 @@ this.perMessageDeflate = null;

send(packets) {
var self = this;
const packet = packets.shift();
if (typeof packet === "undefined") {
this.writable = true;
this.emit("drain");
return;
}
for (var i = 0; i < packets.length; i++) {
var packet = packets[i];
this.parser.encodePacket(packet, self.supportsBinary, send);
// always creates a new object since ws modifies it
const opts = {};
if (packet.options) {
opts.compress = packet.options.compress;
}
function send(data) {
debug('writing "%s"', data);
// always creates a new object since ws modifies it
var opts = {};
if (packet.options) {
opts.compress = packet.options.compress;
}
if (self.perMessageDeflate) {
var len =
const send = data => {
if (this.perMessageDeflate) {
const len =
"string" === typeof data ? Buffer.byteLength(data) : data.length;
if (len < self.perMessageDeflate.threshold) {
if (len < this.perMessageDeflate.threshold) {
opts.compress = false;
}
}
debug('writing "%s"', data);
this.writable = false;
self.writable = false;
self.socket.send(data, opts, onEnd);
}
this.socket.send(data, opts, err => {
if (err) return this.onError("write error", err.stack);
this.send(packets);
});
};
function onEnd(err) {
if (err) return self.onError("write error", err.stack);
self.writable = true;
self.emit("drain");
if (packet.options && typeof packet.options.wsPreEncoded === "string") {
send(packet.options.wsPreEncoded);
} else {
this.parser.encodePacket(packet, this.supportsBinary, send);
}

@@ -104,0 +103,0 @@ }

{
"name": "engine.io",
"version": "5.0.0",
"version": "5.1.0",
"description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server",

@@ -39,3 +39,3 @@ "main": "lib/engine.io.js",

"eiows": "^3.3.0",
"engine.io-client": "5.0.0",
"engine.io-client": "5.1.0",
"engine.io-client-v3": "npm:engine.io-client@3.5.0",

@@ -42,0 +42,0 @@ "eslint": "^4.19.1",

@@ -208,2 +208,33 @@

- `initial_headers`
- Fired on the first request of the connection, before writing the response headers
- **Arguments**
- `headers` (`Object`): a hash of headers
- `req` (`http.IncomingMessage`): the request
- `headers`
- Fired on the all requests of the connection, before writing the response headers
- **Arguments**
- `headers` (`Object`): a hash of headers
- `req` (`http.IncomingMessage`): the request
- `connection_error`
- Fired when an error occurs when establishing the connection.
- **Arguments**
- `error`: an object with following properties:
- `req` (`http.IncomingMessage`): the request that was dropped
- `code` (`Number`): one of `Server.errors`
- `message` (`string`): one of `Server.errorMessages`
- `context` (`Object`): extra info about the error
| Code | Message |
| ---- | ------- |
| 0 | "Transport unknown"
| 1 | "Session ID unknown"
| 2 | "Bad handshake method"
| 3 | "Bad request"
| 4 | "Forbidden"
| 5 | "Unsupported protocol version"
##### Properties

@@ -210,0 +241,0 @@

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