Skip to content

Commit

Permalink
feat: add support for Socket.IO v2 clients
Browse files Browse the repository at this point in the history
In order to ease the migration to Socket.IO v3, the Socket.IO server
can now communicate with v2 clients.

```js
const io = require("socket.io")({
  allowEIO3: true
});
```

This feature is disabled by default.
  • Loading branch information
darrachequesne committed Jan 14, 2021
1 parent de8dffd commit 9925746
Show file tree
Hide file tree
Showing 6 changed files with 229 additions and 13 deletions.
11 changes: 10 additions & 1 deletion lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ interface EngineOptions {
* the options that will be forwarded to the cors module
*/
cors: CorsOptions;
/**
* whether to enable compatibility with Socket.IO v2 clients
* @default false
*/
allowEIO3: boolean;
}

interface AttachOptions {
Expand Down Expand Up @@ -522,7 +527,11 @@ export class Server extends EventEmitter {
*/
private onconnection(conn): Server {
debug("incoming connection with id %s", conn.id);
new Client(this, conn);
const client = new Client(this, conn);
if (conn.protocol === 3) {
// @ts-ignore
client.connect("/");
}
return this;
}

Expand Down
15 changes: 10 additions & 5 deletions lib/namespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,16 @@ export class Namespace extends EventEmitter {
this.run(socket, (err) => {
process.nextTick(() => {
if ("open" == client.conn.readyState) {
if (err)
return socket._error({
message: err.message,
data: err.data,
});
if (err) {
if (client.conn.protocol === 3) {
return socket._error(err.data || err.message);
} else {
return socket._error({
message: err.message,
data: err.data,
});
}
}

// track socket
this.sockets.set(socket.id, socket);
Expand Down
13 changes: 11 additions & 2 deletions lib/socket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,12 @@ export class Socket extends EventEmitter {
super();
this.server = nsp.server;
this.adapter = this.nsp.adapter;
this.id = base64id.generateId(); // don't reuse the Engine.IO id because it's sensitive information
if (client.conn.protocol === 3) {
// @ts-ignore
this.id = nsp.name !== "/" ? nsp.name + "#" + client.id : client.id;
} else {
this.id = base64id.generateId(); // don't reuse the Engine.IO id because it's sensitive information
}
this.connected = true;
this.disconnected = false;
this.handshake = this.buildHandshake(auth);
Expand Down Expand Up @@ -286,7 +291,11 @@ export class Socket extends EventEmitter {
_onconnect(): void {
debug("socket connected - writing packet");
this.join(this.id);
this.packet({ type: PacketType.CONNECT, data: { sid: this.id } });
if (this.conn.protocol === 3) {
this.packet({ type: PacketType.CONNECT });
} else {
this.packet({ type: PacketType.CONNECT, data: { sid: this.id } });
}
}

/**
Expand Down
142 changes: 139 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"accepts": "~1.3.4",
"base64id": "~2.0.0",
"debug": "~4.3.1",
"engine.io": "~4.0.6",
"engine.io": "~4.1.0",
"socket.io-adapter": "~2.0.3",
"socket.io-parser": "~4.0.3"
},
Expand All @@ -64,6 +64,7 @@
"prettier": "^2.2.0",
"rimraf": "^3.0.2",
"socket.io-client": "3.0.5",
"socket.io-client-v2": "npm:socket.io-client@^2.4.0",
"superagent": "^6.1.0",
"supertest": "^6.0.1",
"ts-node": "^9.0.0",
Expand Down
58 changes: 57 additions & 1 deletion test/socket.io.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import { exec } from "child_process";
import request from "supertest";
import expect from "expect.js";
import type { AddressInfo } from "net";
import * as io_v2 from "socket.io-client-v2";

const ioc = require("socket.io-client");

import "./support/util";
import exp = require("constants");

// Creates a socket.io client for the given server
function client(srv, nsp?: string | object, opts?: object) {
Expand All @@ -26,6 +26,18 @@ function client(srv, nsp?: string | object, opts?: object) {
return ioc(url, opts);
}

const success = (sio, clientSocket, done) => {
sio.close();
clientSocket.close();
done();
};

const waitFor = (emitter, event) => {
return new Promise((resolve) => {
emitter.once(event, resolve);
});
};

describe("socket.io", () => {
it("should be the same version as client", () => {
const version = require("../package").version;
Expand Down Expand Up @@ -2430,4 +2442,48 @@ describe("socket.io", () => {
});
});
});

describe("v2 compatibility", () => {
it("should connect if `allowEIO3` is true", (done) => {
const srv = createServer();
const sio = new Server(srv, {
allowEIO3: true,
});

srv.listen(async () => {
const port = (srv.address() as AddressInfo).port;
const clientSocket = io_v2.connect(`http://localhost:${port}`, {
multiplex: false,
});

const [socket]: Array<any> = await Promise.all([
waitFor(sio, "connection"),
waitFor(clientSocket, "connect"),
]);

expect(socket.id).to.eql(clientSocket.id);
success(sio, clientSocket, done);
});
});

it("should not connect if `allowEIO3` is false (default)", (done) => {
const srv = createServer();
const sio = new Server(srv);

srv.listen(() => {
const port = (srv.address() as AddressInfo).port;
const clientSocket = io_v2.connect(`http://localhost:${port}`, {
multiplex: false,
});

clientSocket.on("connect", () => {
done(new Error("should not happen"));
});

clientSocket.on("connect_error", () => {
success(sio, clientSocket, done);
});
});
});
});
});

0 comments on commit 9925746

Please sign in to comment.