mirror of
https://github.com/socketio/socket.io.git
synced 2026-01-13 08:57:59 -05:00
Using an async operation with `io.use()` could lead to the creation of
several instances of a same namespace, each of them overriding the
previous one.
Example:
```js
io.use(async (nsp, auth, next) => {
await anOperationThatTakesSomeTime();
next();
});
```
Related: https://github.com/socketio/socket.io/pull/4136
2868 lines
80 KiB
TypeScript
2868 lines
80 KiB
TypeScript
"use strict";
|
|
|
|
import { Server, Socket, Namespace } from "..";
|
|
import { createServer } from "http";
|
|
import fs = require("fs");
|
|
import { join } from "path";
|
|
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";
|
|
import type { SocketId } from "socket.io-adapter";
|
|
import { io as ioc, Socket as ClientSocket } from "socket.io-client";
|
|
|
|
import "./support/util";
|
|
import "./utility-methods";
|
|
|
|
type callback = (err: Error | null, success: boolean) => void;
|
|
|
|
// Creates a socket.io client for the given server
|
|
function client(srv, nsp?: string | object, opts?: object): ClientSocket {
|
|
if ("object" == typeof nsp) {
|
|
opts = nsp;
|
|
nsp = undefined;
|
|
}
|
|
let addr = srv.address();
|
|
if (!addr) addr = srv.listen().address();
|
|
const url = "ws://localhost:" + addr.port + (nsp || "");
|
|
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);
|
|
});
|
|
};
|
|
|
|
const getPort = (io: Server): number => {
|
|
// @ts-ignore
|
|
return io.httpServer.address().port;
|
|
};
|
|
|
|
describe("socket.io", () => {
|
|
it("should be the same version as client", () => {
|
|
const version = require("../package").version;
|
|
expect(version).to.be(require("socket.io-client/package.json").version);
|
|
});
|
|
|
|
describe("server attachment", () => {
|
|
describe("http.Server", () => {
|
|
const clientVersion = require("socket.io-client/package.json").version;
|
|
|
|
const testSource = (filename) => (done) => {
|
|
const srv = createServer();
|
|
new Server(srv);
|
|
request(srv)
|
|
.get("/socket.io/" + filename)
|
|
.buffer(true)
|
|
.end((err, res) => {
|
|
if (err) return done(err);
|
|
expect(res.headers["content-type"]).to.be("application/javascript");
|
|
expect(res.headers.etag).to.be('"' + clientVersion + '"');
|
|
expect(res.headers["x-sourcemap"]).to.be(undefined);
|
|
expect(res.text).to.match(/engine\.io/);
|
|
expect(res.status).to.be(200);
|
|
done();
|
|
});
|
|
};
|
|
|
|
const testSourceMap = (filename) => (done) => {
|
|
const srv = createServer();
|
|
new Server(srv);
|
|
request(srv)
|
|
.get("/socket.io/" + filename)
|
|
.buffer(true)
|
|
.end((err, res) => {
|
|
if (err) return done(err);
|
|
expect(res.headers["content-type"]).to.be("application/json");
|
|
expect(res.headers.etag).to.be('"' + clientVersion + '"');
|
|
expect(res.text).to.match(/engine\.io/);
|
|
expect(res.status).to.be(200);
|
|
done();
|
|
});
|
|
};
|
|
|
|
it("should serve client", testSource("socket.io.js"));
|
|
it(
|
|
"should serve client with query string",
|
|
testSource("socket.io.js?buster=" + Date.now())
|
|
);
|
|
it("should serve source map", testSourceMap("socket.io.js.map"));
|
|
it("should serve client (min)", testSource("socket.io.min.js"));
|
|
|
|
it(
|
|
"should serve source map (min)",
|
|
testSourceMap("socket.io.min.js.map")
|
|
);
|
|
|
|
it("should serve client (gzip)", (done) => {
|
|
const srv = createServer();
|
|
new Server(srv);
|
|
request(srv)
|
|
.get("/socket.io/socket.io.js")
|
|
.set("accept-encoding", "gzip,br,deflate")
|
|
.buffer(true)
|
|
.end((err, res) => {
|
|
if (err) return done(err);
|
|
expect(res.headers["content-encoding"]).to.be("gzip");
|
|
expect(res.status).to.be(200);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it(
|
|
"should serve bundle with msgpack parser",
|
|
testSource("socket.io.msgpack.min.js")
|
|
);
|
|
|
|
it(
|
|
"should serve source map for bundle with msgpack parser",
|
|
testSourceMap("socket.io.msgpack.min.js.map")
|
|
);
|
|
|
|
it("should serve the ESM bundle", testSource("socket.io.esm.min.js"));
|
|
|
|
it(
|
|
"should serve the source map for the ESM bundle",
|
|
testSourceMap("socket.io.esm.min.js.map")
|
|
);
|
|
|
|
it("should handle 304", (done) => {
|
|
const srv = createServer();
|
|
new Server(srv);
|
|
request(srv)
|
|
.get("/socket.io/socket.io.js")
|
|
.set("If-None-Match", '"' + clientVersion + '"')
|
|
.end((err, res) => {
|
|
if (err) return done(err);
|
|
expect(res.statusCode).to.be(304);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("should handle 304", (done) => {
|
|
const srv = createServer();
|
|
new Server(srv);
|
|
request(srv)
|
|
.get("/socket.io/socket.io.js")
|
|
.set("If-None-Match", 'W/"' + clientVersion + '"')
|
|
.end((err, res) => {
|
|
if (err) return done(err);
|
|
expect(res.statusCode).to.be(304);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("should not serve static files", (done) => {
|
|
const srv = createServer();
|
|
new Server(srv, { serveClient: false });
|
|
request(srv).get("/socket.io/socket.io.js").expect(400, done);
|
|
});
|
|
|
|
it("should work with #attach", (done) => {
|
|
const srv = createServer((req, res) => {
|
|
res.writeHead(404);
|
|
res.end();
|
|
});
|
|
const sockets = new Server();
|
|
sockets.attach(srv);
|
|
request(srv)
|
|
.get("/socket.io/socket.io.js")
|
|
.end((err, res) => {
|
|
if (err) return done(err);
|
|
expect(res.status).to.be(200);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("should work with #attach (and merge options)", () => {
|
|
const srv = createServer((req, res) => {
|
|
res.writeHead(404);
|
|
res.end();
|
|
});
|
|
const server = new Server({
|
|
pingTimeout: 6000,
|
|
});
|
|
server.attach(srv, {
|
|
pingInterval: 24000,
|
|
});
|
|
// @ts-ignore
|
|
expect(server.eio.opts.pingTimeout).to.eql(6000);
|
|
// @ts-ignore
|
|
expect(server.eio.opts.pingInterval).to.eql(24000);
|
|
server.close();
|
|
});
|
|
});
|
|
|
|
describe("port", () => {
|
|
it("should be bound", (done) => {
|
|
const io = new Server(0);
|
|
|
|
request(`http://localhost:${getPort(io)}`)
|
|
.get("/socket.io/socket.io.js")
|
|
.expect(200, done);
|
|
});
|
|
|
|
it("with listen", (done) => {
|
|
const io = new Server().listen(0);
|
|
|
|
request(`http://localhost:${getPort(io)}`)
|
|
.get("/socket.io/socket.io.js")
|
|
.expect(200, done);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("handshake", () => {
|
|
const request = require("superagent");
|
|
|
|
it("should send the Access-Control-Allow-xxx headers on OPTIONS request", (done) => {
|
|
const io = new Server(0, {
|
|
cors: {
|
|
origin: "http://localhost:54023",
|
|
methods: ["GET", "POST"],
|
|
allowedHeaders: ["content-type"],
|
|
credentials: true,
|
|
},
|
|
});
|
|
request
|
|
.options(`http://localhost:${getPort(io)}/socket.io/default/`)
|
|
.query({ transport: "polling", EIO: 4 })
|
|
.set("Origin", "http://localhost:54023")
|
|
.end((err, res) => {
|
|
expect(res.status).to.be(204);
|
|
|
|
expect(res.headers["access-control-allow-origin"]).to.be(
|
|
"http://localhost:54023"
|
|
);
|
|
expect(res.headers["access-control-allow-methods"]).to.be("GET,POST");
|
|
expect(res.headers["access-control-allow-headers"]).to.be(
|
|
"content-type"
|
|
);
|
|
expect(res.headers["access-control-allow-credentials"]).to.be("true");
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("should send the Access-Control-Allow-xxx headers on GET request", (done) => {
|
|
const io = new Server(0, {
|
|
cors: {
|
|
origin: "http://localhost:54024",
|
|
methods: ["GET", "POST"],
|
|
allowedHeaders: ["content-type"],
|
|
credentials: true,
|
|
},
|
|
});
|
|
request
|
|
.get(`http://localhost:${getPort(io)}/socket.io/default/`)
|
|
.query({ transport: "polling", EIO: 4 })
|
|
.set("Origin", "http://localhost:54024")
|
|
.end((err, res) => {
|
|
expect(res.status).to.be(200);
|
|
|
|
expect(res.headers["access-control-allow-origin"]).to.be(
|
|
"http://localhost:54024"
|
|
);
|
|
expect(res.headers["access-control-allow-credentials"]).to.be("true");
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("should allow request if custom function in opts.allowRequest returns true", (done) => {
|
|
const io = new Server(0, {
|
|
allowRequest: (req, callback) => callback(null, true),
|
|
});
|
|
|
|
request
|
|
.get(`http://localhost:${getPort(io)}/socket.io/default/`)
|
|
.query({ transport: "polling", EIO: 4 })
|
|
.end((err, res) => {
|
|
expect(res.status).to.be(200);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("should disallow request if custom function in opts.allowRequest returns false", (done) => {
|
|
const io = new Server(0, {
|
|
allowRequest: (req, callback) => callback(null, false),
|
|
});
|
|
request
|
|
.get(`http://localhost:${getPort(io)}/socket.io/default/`)
|
|
.set("origin", "http://foo.example")
|
|
.query({ transport: "polling", EIO: 4 })
|
|
.end((err, res) => {
|
|
expect(res.status).to.be(403);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("close", () => {
|
|
it("should be able to close sio sending a srv", (done) => {
|
|
const httpServer = createServer().listen(0);
|
|
const io = new Server(httpServer);
|
|
const port = getPort(io);
|
|
const net = require("net");
|
|
const server = net.createServer();
|
|
|
|
const clientSocket = client(httpServer, { reconnection: false });
|
|
|
|
clientSocket.on("disconnect", () => {
|
|
expect(io.sockets.sockets.size).to.equal(0);
|
|
server.listen(port);
|
|
});
|
|
|
|
clientSocket.on("connect", () => {
|
|
expect(io.sockets.sockets.size).to.equal(1);
|
|
io.close();
|
|
});
|
|
|
|
server.once("listening", () => {
|
|
// PORT should be free
|
|
server.close((error) => {
|
|
expect(error).to.be(undefined);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should be able to close sio sending a srv", (done) => {
|
|
const io = new Server(0);
|
|
const port = getPort(io);
|
|
const net = require("net");
|
|
const server = net.createServer();
|
|
|
|
const clientSocket = ioc("ws://0.0.0.0:" + port, {
|
|
reconnection: false,
|
|
});
|
|
|
|
clientSocket.on("disconnect", () => {
|
|
expect(io.sockets.sockets.size).to.equal(0);
|
|
server.listen(port);
|
|
});
|
|
|
|
clientSocket.on("connect", () => {
|
|
expect(io.sockets.sockets.size).to.equal(1);
|
|
io.close();
|
|
});
|
|
|
|
server.once("listening", () => {
|
|
// PORT should be free
|
|
server.close((error) => {
|
|
expect(error).to.be(undefined);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("graceful close", () => {
|
|
function fixture(filename) {
|
|
return (
|
|
'"' +
|
|
process.execPath +
|
|
'" "' +
|
|
join(__dirname, "fixtures", filename) +
|
|
'"'
|
|
);
|
|
}
|
|
|
|
it("should stop socket and timers", (done) => {
|
|
exec(fixture("server-close.ts"), done);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("namespaces", () => {
|
|
it("should be accessible through .sockets", () => {
|
|
const sio = new Server();
|
|
expect(sio.sockets).to.be.a(Namespace);
|
|
});
|
|
|
|
it("should be aliased", () => {
|
|
const sio = new Server();
|
|
expect(sio.use).to.be.a("function");
|
|
expect(sio.to).to.be.a("function");
|
|
expect(sio["in"]).to.be.a("function");
|
|
expect(sio.emit).to.be.a("function");
|
|
expect(sio.send).to.be.a("function");
|
|
expect(sio.write).to.be.a("function");
|
|
expect(sio.allSockets).to.be.a("function");
|
|
expect(sio.compress).to.be.a("function");
|
|
});
|
|
|
|
it("should return an immutable broadcast operator", () => {
|
|
const sio = new Server();
|
|
const operator = sio.local.to(["room1", "room2"]).except("room3");
|
|
operator.compress(true).emit("hello");
|
|
operator.volatile.emit("hello");
|
|
operator.to("room4").emit("hello");
|
|
operator.except("room5").emit("hello");
|
|
sio.to("room6").emit("hello");
|
|
// @ts-ignore
|
|
expect(operator.rooms).to.contain("room1", "room2");
|
|
// @ts-ignore
|
|
expect(operator.exceptRooms).to.contain("room3");
|
|
// @ts-ignore
|
|
expect(operator.flags).to.eql({ local: true });
|
|
});
|
|
|
|
it("should automatically connect", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
socket.on("connect", () => {
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should fire a `connection` event", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
sio.on("connection", (socket: Socket) => {
|
|
expect(socket).to.be.a(Socket);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should fire a `connect` event", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
sio.on("connect", (socket) => {
|
|
expect(socket).to.be.a(Socket);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should work with many sockets", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
sio.of("/chat");
|
|
sio.of("/news");
|
|
const chat = client(srv, "/chat");
|
|
const news = client(srv, "/news");
|
|
let total = 2;
|
|
chat.on("connect", () => {
|
|
--total || done();
|
|
});
|
|
news.on("connect", () => {
|
|
--total || done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should be able to equivalently start with "" or "/" on server', (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
let total = 2;
|
|
sio.of("").on("connection", () => {
|
|
--total || done();
|
|
});
|
|
sio.of("abc").on("connection", () => {
|
|
--total || done();
|
|
});
|
|
const c1 = client(srv, "/");
|
|
const c2 = client(srv, "/abc");
|
|
});
|
|
|
|
it('should be equivalent for "" and "/" on client', (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
sio.of("/").on("connection", () => {
|
|
done();
|
|
});
|
|
const c1 = client(srv, "");
|
|
});
|
|
|
|
it("should work with `of` and many sockets", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const chat = client(srv, "/chat");
|
|
const news = client(srv, "/news");
|
|
let total = 2;
|
|
sio.of("/news").on("connection", (socket) => {
|
|
expect(socket).to.be.a(Socket);
|
|
--total || done();
|
|
});
|
|
sio.of("/news").on("connection", (socket) => {
|
|
expect(socket).to.be.a(Socket);
|
|
--total || done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should work with `of` second param", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const chat = client(srv, "/chat");
|
|
const news = client(srv, "/news");
|
|
let total = 2;
|
|
sio.of("/news", (socket) => {
|
|
expect(socket).to.be.a(Socket);
|
|
--total || done();
|
|
});
|
|
sio.of("/news", (socket) => {
|
|
expect(socket).to.be.a(Socket);
|
|
--total || done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should disconnect upon transport disconnection", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const chat = client(srv, "/chat");
|
|
const news = client(srv, "/news");
|
|
let total = 2;
|
|
let totald = 2;
|
|
let s;
|
|
sio.of("/news", (socket) => {
|
|
socket.on("disconnect", (reason) => {
|
|
--totald || done();
|
|
});
|
|
--total || close();
|
|
});
|
|
sio.of("/chat", (socket) => {
|
|
s = socket;
|
|
socket.on("disconnect", (reason) => {
|
|
--totald || done();
|
|
});
|
|
--total || close();
|
|
});
|
|
function close() {
|
|
s.disconnect(true);
|
|
}
|
|
});
|
|
});
|
|
|
|
it("should fire a `disconnecting` event just before leaving all rooms", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
|
|
sio.on("connection", (s) => {
|
|
s.join("a");
|
|
// FIXME not sure why process.nextTick() is needed here
|
|
process.nextTick(() => s.disconnect());
|
|
|
|
let total = 2;
|
|
s.on("disconnecting", (reason) => {
|
|
expect(s.rooms).to.contain(s.id, "a");
|
|
total--;
|
|
});
|
|
|
|
s.on("disconnect", (reason) => {
|
|
expect(s.rooms.size).to.eql(0);
|
|
--total || done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should return error connecting to non-existent namespace", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv, "/doesnotexist");
|
|
socket.on("connect_error", (err) => {
|
|
expect(err.message).to.be("Invalid namespace");
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should not reuse same-namespace connections", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
let connections = 0;
|
|
|
|
srv.listen(() => {
|
|
const clientSocket1 = client(srv);
|
|
const clientSocket2 = client(srv);
|
|
sio.on("connection", () => {
|
|
connections++;
|
|
if (connections === 2) {
|
|
done();
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should find all clients in a namespace", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
const chatSids: string[] = [];
|
|
let otherSid: SocketId | null = null;
|
|
srv.listen(() => {
|
|
const c1 = client(srv, "/chat");
|
|
const c2 = client(srv, "/chat", { forceNew: true });
|
|
const c3 = client(srv, "/other", { forceNew: true });
|
|
let total = 3;
|
|
sio.of("/chat").on("connection", (socket) => {
|
|
chatSids.push(socket.id);
|
|
--total || getSockets();
|
|
});
|
|
sio.of("/other").on("connection", (socket) => {
|
|
otherSid = socket.id;
|
|
--total || getSockets();
|
|
});
|
|
});
|
|
async function getSockets() {
|
|
const sids = await sio.of("/chat").allSockets();
|
|
|
|
expect(sids).to.contain(chatSids[0], chatSids[1]);
|
|
expect(sids).to.not.contain(otherSid);
|
|
done();
|
|
}
|
|
});
|
|
|
|
it("should find all clients in a namespace room", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
let chatFooSid: SocketId | null = null;
|
|
let chatBarSid: SocketId | null = null;
|
|
let otherSid: SocketId | null = null;
|
|
srv.listen(() => {
|
|
const c1 = client(srv, "/chat");
|
|
const c2 = client(srv, "/chat", { forceNew: true });
|
|
const c3 = client(srv, "/other", { forceNew: true });
|
|
let chatIndex = 0;
|
|
let total = 3;
|
|
sio.of("/chat").on("connection", (socket) => {
|
|
if (chatIndex++) {
|
|
socket.join("foo");
|
|
chatFooSid = socket.id;
|
|
--total || getSockets();
|
|
} else {
|
|
socket.join("bar");
|
|
chatBarSid = socket.id;
|
|
--total || getSockets();
|
|
}
|
|
});
|
|
sio.of("/other").on("connection", (socket) => {
|
|
socket.join("foo");
|
|
otherSid = socket.id;
|
|
--total || getSockets();
|
|
});
|
|
});
|
|
async function getSockets() {
|
|
const sids = await sio.of("/chat").in("foo").allSockets();
|
|
|
|
expect(sids).to.contain(chatFooSid);
|
|
expect(sids).to.not.contain(chatBarSid);
|
|
expect(sids).to.not.contain(otherSid);
|
|
done();
|
|
}
|
|
});
|
|
|
|
it("should find all clients across namespace rooms", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
let chatFooSid: SocketId | null = null;
|
|
let chatBarSid: SocketId | null = null;
|
|
let otherSid: SocketId | null = null;
|
|
srv.listen(() => {
|
|
const c1 = client(srv, "/chat");
|
|
const c2 = client(srv, "/chat", { forceNew: true });
|
|
const c3 = client(srv, "/other", { forceNew: true });
|
|
let chatIndex = 0;
|
|
let total = 3;
|
|
sio.of("/chat").on("connection", (socket) => {
|
|
if (chatIndex++) {
|
|
socket.join("foo");
|
|
chatFooSid = socket.id;
|
|
--total || getSockets();
|
|
} else {
|
|
socket.join("bar");
|
|
chatBarSid = socket.id;
|
|
--total || getSockets();
|
|
}
|
|
});
|
|
sio.of("/other").on("connection", (socket) => {
|
|
socket.join("foo");
|
|
otherSid = socket.id;
|
|
--total || getSockets();
|
|
});
|
|
});
|
|
async function getSockets() {
|
|
const sids = await sio.of("/chat").allSockets();
|
|
expect(sids).to.contain(chatFooSid, chatBarSid);
|
|
expect(sids).to.not.contain(otherSid);
|
|
done();
|
|
}
|
|
});
|
|
|
|
it("should not emit volatile event after regular event", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
|
|
let counter = 0;
|
|
srv.listen(() => {
|
|
sio.of("/chat").on("connection", (s) => {
|
|
// Wait to make sure there are no packets being sent for opening the connection
|
|
setTimeout(() => {
|
|
sio.of("/chat").emit("ev", "data");
|
|
sio.of("/chat").volatile.emit("ev", "data");
|
|
}, 50);
|
|
});
|
|
|
|
const socket = client(srv, "/chat");
|
|
socket.on("ev", () => {
|
|
counter++;
|
|
});
|
|
});
|
|
|
|
setTimeout(() => {
|
|
expect(counter).to.be(1);
|
|
done();
|
|
}, 500);
|
|
});
|
|
|
|
it("should emit volatile event", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
|
|
let counter = 0;
|
|
srv.listen(() => {
|
|
sio.of("/chat").on("connection", (s) => {
|
|
// Wait to make sure there are no packets being sent for opening the connection
|
|
setTimeout(() => {
|
|
sio.of("/chat").volatile.emit("ev", "data");
|
|
}, 100);
|
|
});
|
|
|
|
const socket = client(srv, "/chat");
|
|
socket.on("ev", () => {
|
|
counter++;
|
|
});
|
|
});
|
|
|
|
setTimeout(() => {
|
|
expect(counter).to.be(1);
|
|
done();
|
|
}, 500);
|
|
});
|
|
|
|
it("should enable compression by default", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv, "/chat");
|
|
sio.of("/chat").on("connection", (s) => {
|
|
s.conn.once("packetCreate", (packet) => {
|
|
expect(packet.options.compress).to.be(true);
|
|
done();
|
|
});
|
|
sio.of("/chat").emit("woot", "hi");
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should disable compression", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv, "/chat");
|
|
sio.of("/chat").on("connection", (s) => {
|
|
s.conn.once("packetCreate", (packet) => {
|
|
expect(packet.options.compress).to.be(false);
|
|
done();
|
|
});
|
|
sio.of("/chat").compress(false).emit("woot", "hi");
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should throw on reserved event", () => {
|
|
const sio = new Server();
|
|
|
|
expect(() => sio.emit("connect")).to.throwException(
|
|
/"connect" is a reserved event name/
|
|
);
|
|
});
|
|
|
|
it("should close a client without namespace", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv, {
|
|
connectTimeout: 10,
|
|
});
|
|
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
|
|
// @ts-ignore
|
|
socket.io.engine.write = () => {}; // prevent the client from sending a CONNECT packet
|
|
|
|
socket.on("disconnect", () => {
|
|
socket.close();
|
|
sio.close();
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should close a client without namespace (2)", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv, {
|
|
connectTimeout: 100,
|
|
});
|
|
|
|
sio.use((_, next) => {
|
|
next(new Error("nope"));
|
|
});
|
|
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
|
|
const success = () => {
|
|
socket.close();
|
|
sio.close();
|
|
done();
|
|
};
|
|
|
|
socket.on("disconnect", success);
|
|
});
|
|
});
|
|
|
|
it("should exclude a specific socket when emitting", (done) => {
|
|
const srv = createServer();
|
|
const io = new Server(srv);
|
|
|
|
srv.listen(() => {
|
|
const socket1 = client(srv, "/");
|
|
const socket2 = client(srv, "/");
|
|
|
|
socket2.on("a", () => {
|
|
done(new Error("should not happen"));
|
|
});
|
|
socket1.on("a", () => {
|
|
done();
|
|
});
|
|
|
|
socket2.on("connect", () => {
|
|
io.except(socket2.id).emit("a");
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should exclude a specific socket when emitting (in a namespace)", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
|
|
const nsp = sio.of("/nsp");
|
|
|
|
srv.listen(() => {
|
|
const socket1 = client(srv, "/nsp");
|
|
const socket2 = client(srv, "/nsp");
|
|
|
|
socket2.on("a", () => {
|
|
done(new Error("not"));
|
|
});
|
|
socket1.on("a", () => {
|
|
done();
|
|
});
|
|
|
|
socket2.on("connect", () => {
|
|
nsp.except(socket2.id).emit("a");
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should exclude a specific room when emitting", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
|
|
const nsp = sio.of("/nsp");
|
|
|
|
srv.listen(() => {
|
|
const socket1 = client(srv, "/nsp");
|
|
const socket2 = client(srv, "/nsp");
|
|
|
|
socket1.on("a", () => {
|
|
done();
|
|
});
|
|
socket2.on("a", () => {
|
|
done(new Error("not"));
|
|
});
|
|
|
|
nsp.on("connection", (socket) => {
|
|
socket.on("broadcast", () => {
|
|
socket.join("room1");
|
|
nsp.except("room1").emit("a");
|
|
});
|
|
});
|
|
|
|
socket2.emit("broadcast");
|
|
});
|
|
});
|
|
|
|
it("should emit an 'new_namespace' event", (done) => {
|
|
const sio = new Server();
|
|
|
|
sio.on("new_namespace", (namespace) => {
|
|
expect(namespace.name).to.eql("/nsp");
|
|
done();
|
|
});
|
|
|
|
sio.of("/nsp");
|
|
});
|
|
|
|
describe("dynamic namespaces", () => {
|
|
it("should allow connections to dynamic namespaces with a regex", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
let count = 0;
|
|
srv.listen(() => {
|
|
const socket = client(srv, "/dynamic-101");
|
|
let dynamicNsp = sio
|
|
.of(/^\/dynamic-\d+$/)
|
|
.on("connect", (socket) => {
|
|
expect(socket.nsp.name).to.be("/dynamic-101");
|
|
dynamicNsp.emit("hello", 1, "2", { 3: "4" });
|
|
if (++count === 4) done();
|
|
})
|
|
.use((socket, next) => {
|
|
next();
|
|
if (++count === 4) done();
|
|
});
|
|
socket.on("connect_error", (err) => {
|
|
expect().fail();
|
|
});
|
|
socket.on("connect", () => {
|
|
if (++count === 4) done();
|
|
});
|
|
socket.on("hello", (a, b, c) => {
|
|
expect(a).to.eql(1);
|
|
expect(b).to.eql("2");
|
|
expect(c).to.eql({ 3: "4" });
|
|
if (++count === 4) done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should allow connections to dynamic namespaces with a function", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv, "/dynamic-101");
|
|
sio.of((name, query, next) => next(null, "/dynamic-101" === name));
|
|
socket.on("connect", done);
|
|
});
|
|
});
|
|
|
|
it("should disallow connections when no dynamic namespace matches", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv, "/abc");
|
|
sio.of(/^\/dynamic-\d+$/);
|
|
sio.of((name, query, next) => next(null, "/dynamic-101" === name));
|
|
socket.on("connect_error", (err) => {
|
|
expect(err.message).to.be("Invalid namespace");
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should emit an 'new_namespace' event for a dynamic namespace", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
sio.of(/^\/dynamic-\d+$/);
|
|
|
|
sio.on("new_namespace", (namespace) => {
|
|
expect(namespace.name).to.be("/dynamic-101");
|
|
|
|
socket.disconnect();
|
|
srv.close();
|
|
done();
|
|
});
|
|
|
|
const socket = client(srv, "/dynamic-101");
|
|
});
|
|
});
|
|
|
|
it("should handle race conditions with dynamic namespaces (#4136)", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
const counters = {
|
|
connected: 0,
|
|
created: 0,
|
|
events: 0,
|
|
};
|
|
const buffer: callback[] = [];
|
|
sio.on("new_namespace", (namespace) => {
|
|
counters.created++;
|
|
});
|
|
srv.listen(() => {
|
|
const handler = () => {
|
|
if (++counters.events === 2) {
|
|
expect(counters.created).to.equal(1);
|
|
done();
|
|
}
|
|
};
|
|
|
|
sio
|
|
.of((name, query, next) => {
|
|
buffer.push(next);
|
|
if (buffer.length === 2) {
|
|
buffer.forEach((next) => next(null, true));
|
|
}
|
|
})
|
|
.on("connection", (socket) => {
|
|
if (++counters.connected === 2) {
|
|
sio.of("/dynamic-101").emit("message");
|
|
}
|
|
});
|
|
|
|
let one = client(srv, "/dynamic-101");
|
|
let two = client(srv, "/dynamic-101");
|
|
one.on("message", handler);
|
|
two.on("message", handler);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("socket", () => {
|
|
it("should not fire events more than once after manually reconnecting", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const clientSocket = client(srv, { reconnection: false });
|
|
clientSocket.on("connect", function init() {
|
|
clientSocket.off("connect", init);
|
|
clientSocket.io.engine.close();
|
|
|
|
process.nextTick(() => {
|
|
clientSocket.connect();
|
|
});
|
|
clientSocket.on("connect", () => {
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should not fire reconnect_failed event more than once when server closed", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const clientSocket = client(srv, {
|
|
reconnectionAttempts: 3,
|
|
reconnectionDelay: 100,
|
|
});
|
|
clientSocket.on("connect", () => {
|
|
srv.close();
|
|
});
|
|
|
|
clientSocket.io.on("reconnect_failed", () => {
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should receive events", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
sio.on("connection", (s) => {
|
|
s.on("random", (a, b, c) => {
|
|
expect(a).to.be(1);
|
|
expect(b).to.be("2");
|
|
expect(c).to.eql([3]);
|
|
done();
|
|
});
|
|
socket.emit("random", 1, "2", [3]);
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should receive message events through `send`", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
sio.on("connection", (s) => {
|
|
s.on("message", (a) => {
|
|
expect(a).to.be(1337);
|
|
done();
|
|
});
|
|
socket.send(1337);
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should error with null messages", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
sio.on("connection", (s) => {
|
|
s.on("message", (a) => {
|
|
expect(a).to.be(null);
|
|
done();
|
|
});
|
|
socket.send(null);
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should handle transport null messages", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv, { reconnection: false });
|
|
sio.on("connection", (s) => {
|
|
s.on("error", (err) => {
|
|
expect(err).to.be.an(Error);
|
|
s.on("disconnect", (reason) => {
|
|
expect(reason).to.be("forced close");
|
|
done();
|
|
});
|
|
});
|
|
(s as any).client.ondata(null);
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should emit events", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
socket.on("woot", (a) => {
|
|
expect(a).to.be("tobi");
|
|
done();
|
|
});
|
|
sio.on("connection", (s) => {
|
|
s.emit("woot", "tobi");
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should emit events with utf8 multibyte character", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
let i = 0;
|
|
socket.on("hoot", (a) => {
|
|
expect(a).to.be("utf8 — string");
|
|
i++;
|
|
|
|
if (3 == i) {
|
|
done();
|
|
}
|
|
});
|
|
sio.on("connection", (s) => {
|
|
s.emit("hoot", "utf8 — string");
|
|
s.emit("hoot", "utf8 — string");
|
|
s.emit("hoot", "utf8 — string");
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should emit events with binary data", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
let imageData;
|
|
socket.on("doge", (a) => {
|
|
expect(Buffer.isBuffer(a)).to.be(true);
|
|
expect(imageData.length).to.equal(a.length);
|
|
expect(imageData[0]).to.equal(a[0]);
|
|
expect(imageData[imageData.length - 1]).to.equal(a[a.length - 1]);
|
|
done();
|
|
});
|
|
sio.on("connection", (s) => {
|
|
fs.readFile(join(__dirname, "support", "doge.jpg"), (err, data) => {
|
|
if (err) return done(err);
|
|
imageData = data;
|
|
s.emit("doge", data);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should emit events with several types of data (including binary)", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
socket.on("multiple", (a, b, c, d, e, f) => {
|
|
expect(a).to.be(1);
|
|
expect(Buffer.isBuffer(b)).to.be(true);
|
|
expect(c).to.be("3");
|
|
expect(d).to.eql([4]);
|
|
expect(Buffer.isBuffer(e)).to.be(true);
|
|
expect(Buffer.isBuffer(f[0])).to.be(true);
|
|
expect(f[1]).to.be("swag");
|
|
expect(Buffer.isBuffer(f[2])).to.be(true);
|
|
done();
|
|
});
|
|
sio.on("connection", (s) => {
|
|
fs.readFile(join(__dirname, "support", "doge.jpg"), (err, data) => {
|
|
if (err) return done(err);
|
|
const buf = Buffer.from("asdfasdf", "utf8");
|
|
s.emit("multiple", 1, data, "3", [4], buf, [data, "swag", buf]);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should receive events with binary data", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
sio.on("connection", (s) => {
|
|
s.on("buff", (a) => {
|
|
expect(Buffer.isBuffer(a)).to.be(true);
|
|
done();
|
|
});
|
|
const buf = Buffer.from("abcdefg", "utf8");
|
|
socket.emit("buff", buf);
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should receive events with several types of data (including binary)", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
sio.on("connection", (s) => {
|
|
s.on("multiple", (a, b, c, d, e, f) => {
|
|
expect(a).to.be(1);
|
|
expect(Buffer.isBuffer(b)).to.be(true);
|
|
expect(c).to.be("3");
|
|
expect(d).to.eql([4]);
|
|
expect(Buffer.isBuffer(e)).to.be(true);
|
|
expect(Buffer.isBuffer(f[0])).to.be(true);
|
|
expect(f[1]).to.be("swag");
|
|
expect(Buffer.isBuffer(f[2])).to.be(true);
|
|
done();
|
|
});
|
|
fs.readFile(join(__dirname, "support", "doge.jpg"), (err, data) => {
|
|
if (err) return done(err);
|
|
const buf = Buffer.from("asdfasdf", "utf8");
|
|
socket.emit("multiple", 1, data, "3", [4], buf, [
|
|
data,
|
|
"swag",
|
|
buf,
|
|
]);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should not emit volatile event after regular event (polling)", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv, { transports: ["polling"] });
|
|
|
|
let counter = 0;
|
|
srv.listen(() => {
|
|
sio.on("connection", (s) => {
|
|
s.emit("ev", "data");
|
|
s.volatile.emit("ev", "data");
|
|
});
|
|
|
|
const socket = client(srv, { transports: ["polling"] });
|
|
socket.on("ev", () => {
|
|
counter++;
|
|
});
|
|
});
|
|
|
|
setTimeout(() => {
|
|
expect(counter).to.be(1);
|
|
done();
|
|
}, 200);
|
|
});
|
|
|
|
it("should not emit volatile event after regular event (ws)", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv, { transports: ["websocket"] });
|
|
|
|
let counter = 0;
|
|
srv.listen(() => {
|
|
sio.on("connection", (s) => {
|
|
s.emit("ev", "data");
|
|
s.volatile.emit("ev", "data");
|
|
});
|
|
|
|
const socket = client(srv, { transports: ["websocket"] });
|
|
socket.on("ev", () => {
|
|
counter++;
|
|
});
|
|
});
|
|
|
|
setTimeout(() => {
|
|
expect(counter).to.be(1);
|
|
done();
|
|
}, 200);
|
|
});
|
|
|
|
it("should emit volatile event (polling)", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv, { transports: ["polling"] });
|
|
|
|
let counter = 0;
|
|
srv.listen(() => {
|
|
sio.on("connection", (s) => {
|
|
// Wait to make sure there are no packets being sent for opening the connection
|
|
setTimeout(() => {
|
|
s.volatile.emit("ev", "data");
|
|
}, 100);
|
|
});
|
|
|
|
const socket = client(srv, { transports: ["polling"] });
|
|
socket.on("ev", () => {
|
|
counter++;
|
|
});
|
|
});
|
|
|
|
setTimeout(() => {
|
|
expect(counter).to.be(1);
|
|
done();
|
|
}, 500);
|
|
});
|
|
|
|
it("should emit volatile event (ws)", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv, { transports: ["websocket"] });
|
|
|
|
let counter = 0;
|
|
srv.listen(() => {
|
|
sio.on("connection", (s) => {
|
|
// Wait to make sure there are no packets being sent for opening the connection
|
|
setTimeout(() => {
|
|
s.volatile.emit("ev", "data");
|
|
}, 20);
|
|
});
|
|
|
|
const socket = client(srv, { transports: ["websocket"] });
|
|
socket.on("ev", () => {
|
|
counter++;
|
|
});
|
|
});
|
|
|
|
setTimeout(() => {
|
|
expect(counter).to.be(1);
|
|
done();
|
|
}, 200);
|
|
});
|
|
|
|
it("should emit only one consecutive volatile event (polling)", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv, { transports: ["polling"] });
|
|
|
|
let counter = 0;
|
|
srv.listen(() => {
|
|
sio.on("connection", (s) => {
|
|
// Wait to make sure there are no packets being sent for opening the connection
|
|
setTimeout(() => {
|
|
s.volatile.emit("ev", "data");
|
|
s.volatile.emit("ev", "data");
|
|
}, 100);
|
|
});
|
|
|
|
const socket = client(srv, { transports: ["polling"] });
|
|
socket.on("ev", () => {
|
|
counter++;
|
|
});
|
|
});
|
|
|
|
setTimeout(() => {
|
|
expect(counter).to.be(1);
|
|
done();
|
|
}, 500);
|
|
});
|
|
|
|
it("should emit only one consecutive volatile event (ws)", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv, { transports: ["websocket"] });
|
|
|
|
let counter = 0;
|
|
srv.listen(() => {
|
|
sio.on("connection", (s) => {
|
|
// Wait to make sure there are no packets being sent for opening the connection
|
|
setTimeout(() => {
|
|
s.volatile.emit("ev", "data");
|
|
s.volatile.emit("ev", "data");
|
|
}, 20);
|
|
});
|
|
|
|
const socket = client(srv, { transports: ["websocket"] });
|
|
socket.on("ev", () => {
|
|
counter++;
|
|
});
|
|
});
|
|
|
|
setTimeout(() => {
|
|
expect(counter).to.be(1);
|
|
done();
|
|
}, 200);
|
|
});
|
|
|
|
it("should emit only one consecutive volatile event with binary (ws)", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv, { transports: ["websocket"] });
|
|
|
|
let counter = 0;
|
|
srv.listen(() => {
|
|
sio.on("connection", (s) => {
|
|
// Wait to make sure there are no packets being sent for opening the connection
|
|
setTimeout(() => {
|
|
s.volatile.emit("ev", Buffer.from([1, 2, 3]));
|
|
s.volatile.emit("ev", Buffer.from([4, 5, 6]));
|
|
}, 20);
|
|
});
|
|
|
|
const socket = client(srv, { transports: ["websocket"] });
|
|
socket.on("ev", () => {
|
|
counter++;
|
|
});
|
|
});
|
|
|
|
setTimeout(() => {
|
|
expect(counter).to.be(1);
|
|
done();
|
|
}, 200);
|
|
});
|
|
|
|
it("should emit regular events after trying a failed volatile event (polling)", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv, { transports: ["polling"] });
|
|
|
|
let counter = 0;
|
|
srv.listen(() => {
|
|
sio.on("connection", (s) => {
|
|
// Wait to make sure there are no packets being sent for opening the connection
|
|
setTimeout(() => {
|
|
s.emit("ev", "data");
|
|
s.volatile.emit("ev", "data");
|
|
s.emit("ev", "data");
|
|
}, 20);
|
|
});
|
|
|
|
const socket = client(srv, { transports: ["polling"] });
|
|
socket.on("ev", () => {
|
|
counter++;
|
|
});
|
|
});
|
|
|
|
setTimeout(() => {
|
|
expect(counter).to.be(2);
|
|
done();
|
|
}, 200);
|
|
});
|
|
|
|
it("should emit regular events after trying a failed volatile event (ws)", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv, { transports: ["websocket"] });
|
|
|
|
let counter = 0;
|
|
srv.listen(() => {
|
|
sio.on("connection", (s) => {
|
|
// Wait to make sure there are no packets being sent for opening the connection
|
|
setTimeout(() => {
|
|
s.emit("ev", "data");
|
|
s.volatile.emit("ev", "data");
|
|
s.emit("ev", "data");
|
|
}, 20);
|
|
});
|
|
|
|
const socket = client(srv, { transports: ["websocket"] });
|
|
socket.on("ev", () => {
|
|
counter++;
|
|
});
|
|
});
|
|
|
|
setTimeout(() => {
|
|
expect(counter).to.be(2);
|
|
done();
|
|
}, 200);
|
|
});
|
|
|
|
it("should emit message events through `send`", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
socket.on("message", (a) => {
|
|
expect(a).to.be("a");
|
|
done();
|
|
});
|
|
sio.on("connection", (s) => {
|
|
s.send("a");
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should receive event with callbacks", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
sio.on("connection", (s) => {
|
|
s.on("woot", (fn) => {
|
|
fn(1, 2);
|
|
});
|
|
socket.emit("woot", (a, b) => {
|
|
expect(a).to.be(1);
|
|
expect(b).to.be(2);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should receive all events emitted from namespaced client immediately and in order", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
let total = 0;
|
|
srv.listen(() => {
|
|
sio.of("/chat", (s) => {
|
|
s.on("hi", (letter) => {
|
|
total++;
|
|
if (total == 2 && letter == "b") {
|
|
done();
|
|
} else if (total == 1 && letter != "a") {
|
|
throw new Error("events out of order");
|
|
}
|
|
});
|
|
});
|
|
|
|
const chat = client(srv, "/chat");
|
|
chat.emit("hi", "a");
|
|
setTimeout(() => {
|
|
chat.emit("hi", "b");
|
|
}, 50);
|
|
});
|
|
});
|
|
|
|
it("should emit events with callbacks", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
sio.on("connection", (s) => {
|
|
socket.on("hi", (fn) => {
|
|
fn();
|
|
});
|
|
s.emit("hi", () => {
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should receive events with args and callback", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
sio.on("connection", (s) => {
|
|
s.on("woot", (a, b, fn) => {
|
|
expect(a).to.be(1);
|
|
expect(b).to.be(2);
|
|
fn();
|
|
});
|
|
socket.emit("woot", 1, 2, () => {
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should emit events with args and callback", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
sio.on("connection", (s) => {
|
|
socket.on("hi", (a, b, fn) => {
|
|
expect(a).to.be(1);
|
|
expect(b).to.be(2);
|
|
fn();
|
|
});
|
|
s.emit("hi", 1, 2, () => {
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should receive events with binary args and callbacks", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
sio.on("connection", (s) => {
|
|
s.on("woot", (buf, fn) => {
|
|
expect(Buffer.isBuffer(buf)).to.be(true);
|
|
fn(1, 2);
|
|
});
|
|
socket.emit("woot", Buffer.alloc(3), (a, b) => {
|
|
expect(a).to.be(1);
|
|
expect(b).to.be(2);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should emit events with binary args and callback", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
sio.on("connection", (s) => {
|
|
socket.on("hi", (a, fn) => {
|
|
expect(Buffer.isBuffer(a)).to.be(true);
|
|
fn();
|
|
});
|
|
s.emit("hi", Buffer.alloc(4), () => {
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should emit events and receive binary data in a callback", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
sio.on("connection", (s) => {
|
|
socket.on("hi", (fn) => {
|
|
fn(Buffer.alloc(1));
|
|
});
|
|
s.emit("hi", (a) => {
|
|
expect(Buffer.isBuffer(a)).to.be(true);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should receive events and pass binary data in a callback", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
sio.on("connection", (s) => {
|
|
s.on("woot", (fn) => {
|
|
fn(Buffer.alloc(2));
|
|
});
|
|
socket.emit("woot", (a) => {
|
|
expect(Buffer.isBuffer(a)).to.be(true);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should have access to the client", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
sio.on("connection", (s) => {
|
|
expect(s.client).to.be.an("object");
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should have access to the connection", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
sio.on("connection", (s) => {
|
|
expect(s.client.conn).to.be.an("object");
|
|
expect(s.conn).to.be.an("object");
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should have access to the request", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
sio.on("connection", (s) => {
|
|
expect(s.client.request.headers).to.be.an("object");
|
|
expect(s.request.headers).to.be.an("object");
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should see query parameters in the request", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv, { query: { key1: 1, key2: 2 } });
|
|
sio.on("connection", (s) => {
|
|
const parsed = require("url").parse(s.request.url);
|
|
const query = require("querystring").parse(parsed.query);
|
|
expect(query.key1).to.be("1");
|
|
expect(query.key2).to.be("2");
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should see query parameters sent from secondary namespace connections in handshake object", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
const client1 = client(srv);
|
|
const client2 = client(srv, "/connection2", {
|
|
auth: { key1: "aa", key2: "&=bb" },
|
|
});
|
|
sio.on("connection", (s) => {});
|
|
sio.of("/connection2").on("connection", (s) => {
|
|
expect(s.handshake.query.key1).to.be(undefined);
|
|
expect(s.handshake.query.EIO).to.be("4");
|
|
expect(s.handshake.auth.key1).to.be("aa");
|
|
expect(s.handshake.auth.key2).to.be("&=bb");
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("should handle very large json", function (done) {
|
|
this.timeout(30000);
|
|
const srv = createServer();
|
|
const sio = new Server(srv, { perMessageDeflate: false });
|
|
let received = 0;
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
socket.on("big", (a) => {
|
|
expect(Buffer.isBuffer(a.json)).to.be(false);
|
|
if (++received == 3) done();
|
|
else socket.emit("big", a);
|
|
});
|
|
sio.on("connection", (s) => {
|
|
fs.readFile(
|
|
join(__dirname, "fixtures", "big.json"),
|
|
(err, data: any) => {
|
|
if (err) return done(err);
|
|
data = JSON.parse(data);
|
|
s.emit("big", { hello: "friend", json: data });
|
|
}
|
|
);
|
|
s.on("big", (a) => {
|
|
s.emit("big", a);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should handle very large binary data", function (done) {
|
|
this.timeout(30000);
|
|
const srv = createServer();
|
|
const sio = new Server(srv, { perMessageDeflate: false });
|
|
let received = 0;
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
socket.on("big", (a) => {
|
|
expect(Buffer.isBuffer(a.image)).to.be(true);
|
|
if (++received == 3) done();
|
|
else socket.emit("big", a);
|
|
});
|
|
sio.on("connection", (s) => {
|
|
fs.readFile(join(__dirname, "fixtures", "big.jpg"), (err, data) => {
|
|
if (err) return done(err);
|
|
s.emit("big", { hello: "friend", image: data });
|
|
});
|
|
s.on("big", (a) => {
|
|
expect(Buffer.isBuffer(a.image)).to.be(true);
|
|
s.emit("big", a);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should be able to emit after server close and restart", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
|
|
sio.on("connection", (socket) => {
|
|
socket.on("ev", (data) => {
|
|
expect(data).to.be("payload");
|
|
done();
|
|
});
|
|
});
|
|
|
|
srv.listen(() => {
|
|
const { port } = srv.address() as AddressInfo;
|
|
const clientSocket = client(srv, {
|
|
reconnectionAttempts: 10,
|
|
reconnectionDelay: 100,
|
|
});
|
|
clientSocket.once("connect", () => {
|
|
srv.close(() => {
|
|
clientSocket.io.on("reconnect", () => {
|
|
clientSocket.emit("ev", "payload");
|
|
});
|
|
sio.listen(port);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should enable compression by default", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv, "/chat");
|
|
sio.of("/chat").on("connection", (s) => {
|
|
s.conn.once("packetCreate", (packet) => {
|
|
expect(packet.options.compress).to.be(true);
|
|
done();
|
|
});
|
|
sio.of("/chat").emit("woot", "hi");
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should disable compression", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv, "/chat");
|
|
sio.of("/chat").on("connection", (s) => {
|
|
s.conn.once("packetCreate", (packet) => {
|
|
expect(packet.options.compress).to.be(false);
|
|
done();
|
|
});
|
|
sio.of("/chat").compress(false).emit("woot", "hi");
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should error with raw binary and warn", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv, { reconnection: false });
|
|
sio.on("connection", (s) => {
|
|
s.conn.on("upgrade", () => {
|
|
console.log(
|
|
"\u001b[96mNote: warning expected and normal in test.\u001b[39m"
|
|
);
|
|
// @ts-ignore
|
|
socket.io.engine.write("5woooot");
|
|
setTimeout(() => {
|
|
done();
|
|
}, 100);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should not crash when receiving an error packet without handler", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv, { reconnection: false });
|
|
sio.on("connection", (s) => {
|
|
s.conn.on("upgrade", () => {
|
|
console.log(
|
|
"\u001b[96mNote: warning expected and normal in test.\u001b[39m"
|
|
);
|
|
// @ts-ignore
|
|
socket.io.engine.write('44["handle me please"]');
|
|
setTimeout(() => {
|
|
done();
|
|
}, 100);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should not crash with raw binary", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv, { reconnection: false });
|
|
sio.on("connection", (s) => {
|
|
s.once("error", (err) => {
|
|
expect(err.message).to.match(/Illegal attachments/);
|
|
done();
|
|
});
|
|
s.conn.on("upgrade", () => {
|
|
// @ts-ignore
|
|
socket.io.engine.write("5woooot");
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should handle empty binary packet", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv, { reconnection: false });
|
|
sio.on("connection", (s) => {
|
|
s.once("error", (err) => {
|
|
expect(err.message).to.match(/Illegal attachments/);
|
|
done();
|
|
});
|
|
s.conn.on("upgrade", () => {
|
|
// @ts-ignore
|
|
socket.io.engine.write("5");
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should not crash when messing with Object prototype (and other globals)", (done) => {
|
|
// @ts-ignore
|
|
Object.prototype.foo = "bar";
|
|
// @ts-ignore
|
|
global.File = "";
|
|
// @ts-ignore
|
|
global.Blob = [];
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
|
|
sio.on("connection", (s) => {
|
|
s.disconnect(true);
|
|
sio.close();
|
|
setTimeout(() => {
|
|
done();
|
|
}, 100);
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should throw on reserved event", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
sio.on("connection", (s) => {
|
|
expect(() => s.emit("connect_error")).to.throwException(
|
|
/"connect_error" is a reserved event name/
|
|
);
|
|
socket.close();
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should ignore a packet received after disconnection", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
|
|
srv.listen(() => {
|
|
const clientSocket = client(srv);
|
|
|
|
const success = () => {
|
|
clientSocket.close();
|
|
sio.close();
|
|
done();
|
|
};
|
|
|
|
sio.on("connection", (socket) => {
|
|
socket.on("test", () => {
|
|
done(new Error("should not happen"));
|
|
});
|
|
socket.on("disconnect", success);
|
|
});
|
|
|
|
clientSocket.on("connect", () => {
|
|
clientSocket.emit("test", Buffer.alloc(10));
|
|
clientSocket.disconnect();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("onAny", () => {
|
|
it("should call listener", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
|
|
srv.listen(() => {
|
|
const socket = client(srv, { multiplex: false });
|
|
|
|
socket.emit("my-event", "123");
|
|
|
|
sio.on("connection", (socket: Socket) => {
|
|
socket.onAny((event, arg1) => {
|
|
expect(event).to.be("my-event");
|
|
expect(arg1).to.be("123");
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should prepend listener", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
|
|
srv.listen(() => {
|
|
const socket = client(srv, { multiplex: false });
|
|
|
|
socket.emit("my-event", "123");
|
|
|
|
sio.on("connection", (socket: Socket) => {
|
|
let count = 0;
|
|
|
|
socket.onAny((event, arg1) => {
|
|
expect(count).to.be(2);
|
|
done();
|
|
});
|
|
|
|
socket.prependAny(() => {
|
|
expect(count++).to.be(1);
|
|
});
|
|
|
|
socket.prependAny(() => {
|
|
expect(count++).to.be(0);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should remove listener", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
|
|
srv.listen(() => {
|
|
const socket = client(srv, { multiplex: false });
|
|
|
|
socket.emit("my-event", "123");
|
|
|
|
sio.on("connection", (socket: Socket) => {
|
|
const fail = () => done(new Error("fail"));
|
|
|
|
socket.onAny(fail);
|
|
socket.offAny(fail);
|
|
expect(socket.listenersAny.length).to.be(0);
|
|
|
|
socket.onAny(() => {
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("messaging many", () => {
|
|
it("emits to a namespace", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
let total = 2;
|
|
|
|
srv.listen(() => {
|
|
const socket1 = client(srv, { multiplex: false });
|
|
const socket2 = client(srv, { multiplex: false });
|
|
const socket3 = client(srv, "/test");
|
|
socket1.on("a", (a) => {
|
|
expect(a).to.be("b");
|
|
--total || done();
|
|
});
|
|
socket2.on("a", (a) => {
|
|
expect(a).to.be("b");
|
|
--total || done();
|
|
});
|
|
socket3.on("a", () => {
|
|
done(new Error("not"));
|
|
});
|
|
|
|
let sockets = 3;
|
|
sio.on("connection", (socket) => {
|
|
--sockets || emit();
|
|
});
|
|
sio.of("/test", (socket) => {
|
|
--sockets || emit();
|
|
});
|
|
|
|
function emit() {
|
|
sio.emit("a", "b");
|
|
}
|
|
});
|
|
});
|
|
|
|
it("emits binary data to a namespace", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
let total = 2;
|
|
|
|
srv.listen(() => {
|
|
const socket1 = client(srv, { multiplex: false });
|
|
const socket2 = client(srv, { multiplex: false });
|
|
const socket3 = client(srv, "/test");
|
|
socket1.on("bin", (a) => {
|
|
expect(Buffer.isBuffer(a)).to.be(true);
|
|
--total || done();
|
|
});
|
|
socket2.on("bin", (a) => {
|
|
expect(Buffer.isBuffer(a)).to.be(true);
|
|
--total || done();
|
|
});
|
|
socket3.on("bin", () => {
|
|
done(new Error("not"));
|
|
});
|
|
|
|
let sockets = 3;
|
|
sio.on("connection", (socket) => {
|
|
--sockets || emit();
|
|
});
|
|
sio.of("/test", (socket) => {
|
|
--sockets || emit();
|
|
});
|
|
|
|
function emit() {
|
|
sio.emit("bin", Buffer.alloc(10));
|
|
}
|
|
});
|
|
});
|
|
|
|
it("emits to the rest", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
const total = 2;
|
|
|
|
srv.listen(() => {
|
|
const socket1 = client(srv, { multiplex: false });
|
|
const socket2 = client(srv, { multiplex: false });
|
|
const socket3 = client(srv, "/test");
|
|
socket1.on("a", (a) => {
|
|
expect(a).to.be("b");
|
|
socket1.emit("finish");
|
|
});
|
|
socket2.emit("broadcast");
|
|
socket2.on("a", () => {
|
|
done(new Error("done"));
|
|
});
|
|
socket3.on("a", () => {
|
|
done(new Error("not"));
|
|
});
|
|
|
|
const sockets = 2;
|
|
sio.on("connection", (socket) => {
|
|
socket.on("broadcast", () => {
|
|
socket.broadcast.emit("a", "b");
|
|
});
|
|
socket.on("finish", () => {
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("emits to rooms", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
const total = 2;
|
|
|
|
srv.listen(() => {
|
|
const socket1 = client(srv, { multiplex: false });
|
|
const socket2 = client(srv, { multiplex: false });
|
|
|
|
socket2.on("a", () => {
|
|
done(new Error("not"));
|
|
});
|
|
socket1.on("a", () => {
|
|
done();
|
|
});
|
|
socket1.emit("join", "woot");
|
|
socket1.emit("emit", "woot");
|
|
|
|
sio.on("connection", (socket) => {
|
|
socket.on("join", (room, fn) => {
|
|
socket.join(room);
|
|
fn && fn();
|
|
});
|
|
|
|
socket.on("emit", (room) => {
|
|
sio.in(room).emit("a");
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("emits to rooms avoiding dupes", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
let total = 2;
|
|
|
|
srv.listen(() => {
|
|
const socket1 = client(srv, { multiplex: false });
|
|
const socket2 = client(srv, { multiplex: false });
|
|
|
|
socket2.on("a", () => {
|
|
done(new Error("not"));
|
|
});
|
|
socket1.on("a", () => {
|
|
--total || done();
|
|
});
|
|
socket2.on("b", () => {
|
|
--total || done();
|
|
});
|
|
|
|
socket1.emit("join", "woot");
|
|
socket1.emit("join", "test");
|
|
socket2.emit("join", "third", () => {
|
|
socket2.emit("emit");
|
|
});
|
|
|
|
sio.on("connection", (socket) => {
|
|
socket.on("join", (room, fn) => {
|
|
socket.join(room);
|
|
fn && fn();
|
|
});
|
|
|
|
socket.on("emit", (room) => {
|
|
sio.in("woot").in("test").emit("a");
|
|
sio.in("third").emit("b");
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("broadcasts to rooms", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
let total = 2;
|
|
|
|
srv.listen(() => {
|
|
const socket1 = client(srv, { multiplex: false });
|
|
const socket2 = client(srv, { multiplex: false });
|
|
const socket3 = client(srv, { multiplex: false });
|
|
|
|
socket1.emit("join", "woot");
|
|
socket2.emit("join", "test");
|
|
socket3.emit("join", "test", () => {
|
|
socket3.emit("broadcast");
|
|
});
|
|
|
|
socket1.on("a", () => {
|
|
done(new Error("not"));
|
|
});
|
|
socket2.on("a", () => {
|
|
--total || done();
|
|
});
|
|
socket3.on("a", () => {
|
|
done(new Error("not"));
|
|
});
|
|
socket3.on("b", () => {
|
|
--total || done();
|
|
});
|
|
|
|
sio.on("connection", (socket) => {
|
|
socket.on("join", (room, fn) => {
|
|
socket.join(room);
|
|
fn && fn();
|
|
});
|
|
|
|
socket.on("broadcast", () => {
|
|
socket.broadcast.to("test").emit("a");
|
|
socket.emit("b");
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("broadcasts binary data to rooms", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
let total = 2;
|
|
|
|
srv.listen(() => {
|
|
const socket1 = client(srv, { multiplex: false });
|
|
const socket2 = client(srv, { multiplex: false });
|
|
const socket3 = client(srv, { multiplex: false });
|
|
|
|
socket1.emit("join", "woot");
|
|
socket2.emit("join", "test");
|
|
socket3.emit("join", "test", () => {
|
|
socket3.emit("broadcast");
|
|
});
|
|
|
|
socket1.on("bin", (data) => {
|
|
throw new Error("got bin in socket1");
|
|
});
|
|
socket2.on("bin", (data) => {
|
|
expect(Buffer.isBuffer(data)).to.be(true);
|
|
--total || done();
|
|
});
|
|
socket2.on("bin2", (data) => {
|
|
throw new Error("socket2 got bin2");
|
|
});
|
|
socket3.on("bin", (data) => {
|
|
throw new Error("socket3 got bin");
|
|
});
|
|
socket3.on("bin2", (data) => {
|
|
expect(Buffer.isBuffer(data)).to.be(true);
|
|
--total || done();
|
|
});
|
|
|
|
sio.on("connection", (socket) => {
|
|
socket.on("join", (room, fn) => {
|
|
socket.join(room);
|
|
fn && fn();
|
|
});
|
|
socket.on("broadcast", () => {
|
|
socket.broadcast.to("test").emit("bin", Buffer.alloc(5));
|
|
socket.emit("bin2", Buffer.alloc(5));
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("keeps track of rooms", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
sio.on("connection", (s) => {
|
|
s.join("a");
|
|
expect(s.rooms).to.contain(s.id, "a");
|
|
s.join("b");
|
|
expect(s.rooms).to.contain(s.id, "a", "b");
|
|
s.join("c");
|
|
expect(s.rooms).to.contain(s.id, "a", "b", "c");
|
|
s.leave("b");
|
|
expect(s.rooms).to.contain(s.id, "a", "c");
|
|
(s as any).leaveAll();
|
|
expect(s.rooms.size).to.eql(0);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("deletes empty rooms", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
sio.on("connection", (s) => {
|
|
s.join("a");
|
|
expect(s.nsp.adapter.rooms).to.contain("a");
|
|
s.leave("a");
|
|
expect(s.nsp.adapter.rooms).to.not.contain("a");
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should properly cleanup left rooms", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
sio.on("connection", (s) => {
|
|
s.join("a");
|
|
expect(s.rooms).to.contain(s.id, "a");
|
|
s.join("b");
|
|
expect(s.rooms).to.contain(s.id, "a", "b");
|
|
s.leave("unknown");
|
|
expect(s.rooms).to.contain(s.id, "a", "b");
|
|
(s as any).leaveAll();
|
|
expect(s.rooms.size).to.eql(0);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("allows to join several rooms at once", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
sio.on("connection", (s) => {
|
|
s.join(["a", "b", "c"]);
|
|
expect(s.rooms).to.contain(s.id, "a", "b", "c");
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should exclude specific sockets when broadcasting", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
|
|
srv.listen(() => {
|
|
const socket1 = client(srv, { multiplex: false });
|
|
const socket2 = client(srv, { multiplex: false });
|
|
const socket3 = client(srv, { multiplex: false });
|
|
|
|
socket2.on("a", () => {
|
|
done(new Error("not"));
|
|
});
|
|
socket3.on("a", () => {
|
|
done(new Error("not"));
|
|
});
|
|
socket1.on("a", () => {
|
|
done();
|
|
});
|
|
|
|
sio.on("connection", (socket) => {
|
|
socket.on("exclude", (id) => {
|
|
socket.broadcast.except(id).emit("a");
|
|
});
|
|
});
|
|
|
|
socket2.on("connect", () => {
|
|
socket3.emit("exclude", socket2.id);
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should exclude a specific room when broadcasting", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
|
|
srv.listen(() => {
|
|
const socket1 = client(srv, { multiplex: false });
|
|
const socket2 = client(srv, { multiplex: false });
|
|
const socket3 = client(srv, { multiplex: false });
|
|
|
|
socket2.on("a", () => {
|
|
done(new Error("not"));
|
|
});
|
|
socket3.on("a", () => {
|
|
done(new Error("not"));
|
|
});
|
|
socket1.on("a", () => {
|
|
done();
|
|
});
|
|
|
|
sio.on("connection", (socket) => {
|
|
socket.on("join", (room, cb) => {
|
|
socket.join(room);
|
|
cb();
|
|
});
|
|
socket.on("broadcast", () => {
|
|
socket.broadcast.except("room1").emit("a");
|
|
});
|
|
});
|
|
|
|
socket2.emit("join", "room1", () => {
|
|
socket3.emit("broadcast");
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should return an immutable broadcast operator", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
|
|
srv.listen(() => {
|
|
const clientSocket = client(srv, { multiplex: false });
|
|
|
|
sio.on("connection", (socket: Socket) => {
|
|
const operator = socket.local
|
|
.compress(false)
|
|
.to(["room1", "room2"])
|
|
.except("room3");
|
|
operator.compress(true).emit("hello");
|
|
operator.volatile.emit("hello");
|
|
operator.to("room4").emit("hello");
|
|
operator.except("room5").emit("hello");
|
|
socket.emit("hello");
|
|
socket.to("room6").emit("hello");
|
|
// @ts-ignore
|
|
expect(operator.rooms).to.contain("room1", "room2");
|
|
// @ts-ignore
|
|
expect(operator.rooms).to.not.contain("room4", "room5", "room6");
|
|
// @ts-ignore
|
|
expect(operator.exceptRooms).to.contain("room3");
|
|
// @ts-ignore
|
|
expect(operator.flags).to.eql({ local: true, compress: false });
|
|
|
|
clientSocket.close();
|
|
sio.close();
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should pre encode a broadcast packet", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
|
|
srv.listen(() => {
|
|
const clientSocket = client(srv, { multiplex: false });
|
|
|
|
sio.on("connection", (socket) => {
|
|
socket.conn.on("packetCreate", (packet) => {
|
|
expect(packet.data).to.eql('2["hello","world"]');
|
|
expect(packet.options.wsPreEncoded).to.eql('42["hello","world"]');
|
|
|
|
clientSocket.close();
|
|
sio.close();
|
|
done();
|
|
});
|
|
|
|
sio.emit("hello", "world");
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("middleware", () => {
|
|
it("should call functions", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
let run = 0;
|
|
sio.use((socket, next) => {
|
|
expect(socket).to.be.a(Socket);
|
|
run++;
|
|
next();
|
|
});
|
|
sio.use((socket, next) => {
|
|
expect(socket).to.be.a(Socket);
|
|
run++;
|
|
next();
|
|
});
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
socket.on("connect", () => {
|
|
expect(run).to.be(2);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should pass errors", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
const run = 0;
|
|
sio.use((socket, next) => {
|
|
next(new Error("Authentication error"));
|
|
});
|
|
sio.use((socket, next) => {
|
|
done(new Error("nope"));
|
|
});
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
socket.on("connect", () => {
|
|
done(new Error("nope"));
|
|
});
|
|
socket.on("connect_error", (err) => {
|
|
expect(err.message).to.be("Authentication error");
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should pass an object", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
sio.use((socket, next) => {
|
|
const err = new Error("Authentication error");
|
|
// @ts-ignore
|
|
err.data = { a: "b", c: 3 };
|
|
next(err);
|
|
});
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
socket.on("connect", () => {
|
|
done(new Error("nope"));
|
|
});
|
|
socket.on("connect_error", (err) => {
|
|
expect(err).to.be.an(Error);
|
|
expect(err.message).to.eql("Authentication error");
|
|
// @ts-ignore
|
|
expect(err.data).to.eql({ a: "b", c: 3 });
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should only call connection after fns", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
sio.use((socket: any, next) => {
|
|
socket.name = "guillermo";
|
|
next();
|
|
});
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
sio.on("connection", (socket) => {
|
|
expect((socket as any).name).to.be("guillermo");
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should only call connection after (lengthy) fns", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
let authenticated = false;
|
|
|
|
sio.use((socket, next) => {
|
|
setTimeout(() => {
|
|
authenticated = true;
|
|
next();
|
|
}, 300);
|
|
});
|
|
srv.listen(() => {
|
|
const socket = client(srv);
|
|
socket.on("connect", () => {
|
|
expect(authenticated).to.be(true);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should be ignored if socket gets closed", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
let socket;
|
|
sio.use((s, next) => {
|
|
socket.io.engine.close();
|
|
s.client.conn.on("close", () => {
|
|
process.nextTick(next);
|
|
setTimeout(() => {
|
|
done();
|
|
}, 50);
|
|
});
|
|
});
|
|
srv.listen(() => {
|
|
socket = client(srv);
|
|
sio.on("connection", (socket) => {
|
|
done(new Error("should not fire"));
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should call functions in expected order", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
const result: number[] = [];
|
|
|
|
sio.use(() => {
|
|
done(new Error("should not fire"));
|
|
});
|
|
sio.of("/chat").use((socket, next) => {
|
|
result.push(1);
|
|
setTimeout(next, 50);
|
|
});
|
|
sio.of("/chat").use((socket, next) => {
|
|
result.push(2);
|
|
setTimeout(next, 50);
|
|
});
|
|
sio.of("/chat").use((socket, next) => {
|
|
result.push(3);
|
|
setTimeout(next, 50);
|
|
});
|
|
|
|
srv.listen(() => {
|
|
const chat = client(srv, "/chat");
|
|
chat.on("connect", () => {
|
|
expect(result).to.eql([1, 2, 3]);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should disable the merge of handshake packets", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server();
|
|
sio.use((socket, next) => {
|
|
next();
|
|
});
|
|
sio.listen(srv);
|
|
const socket = client(srv);
|
|
socket.on("connect", () => {
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("should work with a custom namespace", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server();
|
|
sio.listen(srv);
|
|
sio.of("/chat").use((socket, next) => {
|
|
next();
|
|
});
|
|
|
|
let count = 0;
|
|
client(srv, "/").on("connect", () => {
|
|
if (++count === 2) done();
|
|
});
|
|
client(srv, "/chat").on("connect", () => {
|
|
if (++count === 2) done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("socket middleware", () => {
|
|
it("should call functions", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
let run = 0;
|
|
|
|
srv.listen(() => {
|
|
const socket = client(srv, { multiplex: false });
|
|
|
|
socket.emit("join", "woot");
|
|
|
|
sio.on("connection", (socket) => {
|
|
socket.use((event, next) => {
|
|
expect(event).to.eql(["join", "woot"]);
|
|
event.unshift("wrap");
|
|
run++;
|
|
next();
|
|
});
|
|
socket.use((event, next) => {
|
|
expect(event).to.eql(["wrap", "join", "woot"]);
|
|
run++;
|
|
next();
|
|
});
|
|
socket.on("wrap", (data1, data2) => {
|
|
expect(data1).to.be("join");
|
|
expect(data2).to.be("woot");
|
|
expect(run).to.be(2);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should pass errors", (done) => {
|
|
const srv = createServer();
|
|
const sio = new Server(srv);
|
|
|
|
srv.listen(() => {
|
|
const socket = client(srv, { multiplex: false });
|
|
|
|
socket.emit("join", "woot");
|
|
|
|
const success = () => {
|
|
socket.close();
|
|
sio.close();
|
|
done();
|
|
};
|
|
|
|
sio.on("connection", (socket) => {
|
|
socket.use((event, next) => {
|
|
next(new Error("Authentication error"));
|
|
});
|
|
socket.use((event, next) => {
|
|
done(new Error("should not happen"));
|
|
});
|
|
socket.on("join", () => {
|
|
done(new Error("should not happen"));
|
|
});
|
|
socket.on("error", (err) => {
|
|
expect(err).to.be.an(Error);
|
|
expect(err.message).to.eql("Authentication error");
|
|
success();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
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 be able to connect to a namespace with a query", (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}/the-namespace`,
|
|
{
|
|
multiplex: false,
|
|
}
|
|
);
|
|
clientSocket.query = { test: "123" };
|
|
|
|
const [socket]: Array<any> = await Promise.all([
|
|
waitFor(sio.of("/the-namespace"), "connection"),
|
|
waitFor(clientSocket, "connect"),
|
|
]);
|
|
|
|
expect(socket.handshake.auth).to.eql({ test: "123" });
|
|
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);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|