Skip to content

Commit

Permalink
feat: emit an Error object upon middleware error
Browse files Browse the repository at this point in the history
This commit restores the ability to send additional data in the
middleware functions, which was removed during the rewrite to
Typescript ([1]).

The only difference with the previous implementation is that the client
will now emit a "connect_error" (previously, "error") event with an
actual Error object, with both the message and an optional "data"
attribute.

```js
// server-side
io.use((socket, next) => {
  const err = new Error("not authorized");
  err.data = { content: "Please retry later" };
  next(err);
});

// client-side
socket.on("connect_error", err => {
  console.log(err.message); // not authorized
  console.log(err.data.content); // Please retry later
});
```

[1]: a5581a9
  • Loading branch information
darrachequesne committed Oct 30, 2020
1 parent aa7574f commit 54bf4a4
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 10 deletions.
4 changes: 3 additions & 1 deletion lib/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ export class Client {
this._packet({
type: PacketType.CONNECT_ERROR,
nsp: name,
data: "Invalid namespace"
data: {
message: "Invalid namespace"
}
});
}
});
Expand Down
4 changes: 2 additions & 2 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import path from "path";
import engine from "engine.io";
import { Client } from "./client";
import { EventEmitter } from "events";
import { Namespace } from "./namespace";
import { ExtendedError, Namespace } from "./namespace";
import { ParentNamespace } from "./parent-namespace";
import { Adapter, Room, SocketId } from "socket.io-adapter";
import * as parser from "socket.io-parser";
Expand Down Expand Up @@ -593,7 +593,7 @@ export class Server extends EventEmitter {
* @public
*/
public use(
fn: (socket: Socket, next: (err?: Error) => void) => void
fn: (socket: Socket, next: (err?: ExtendedError) => void) => void
): Server {
this.sockets.use(fn);
return this;
Expand Down
18 changes: 14 additions & 4 deletions lib/namespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import { Adapter, Room, SocketId } from "socket.io-adapter";

const debug = debugModule("socket.io:namespace");

export interface ExtendedError extends Error {
data?: any;
}

export class Namespace extends EventEmitter {
public readonly name: string;
public readonly sockets: Map<SocketId, Socket> = new Map();
Expand All @@ -18,7 +22,9 @@ export class Namespace extends EventEmitter {
readonly server: Server;

/** @private */
_fns: Array<(socket: Socket, next: (err: Error) => void) => void> = [];
_fns: Array<
(socket: Socket, next: (err: ExtendedError) => void) => void
> = [];

/** @private */
_rooms: Set<Room> = new Set();
Expand Down Expand Up @@ -60,7 +66,7 @@ export class Namespace extends EventEmitter {
* @public
*/
public use(
fn: (socket: Socket, next: (err?: Error) => void) => void
fn: (socket: Socket, next: (err?: ExtendedError) => void) => void
): Namespace {
this._fns.push(fn);
return this;
Expand All @@ -73,7 +79,7 @@ export class Namespace extends EventEmitter {
* @param {Function} fn - last fn call in the middleware
* @private
*/
private run(socket: Socket, fn: (err: Error) => void) {
private run(socket: Socket, fn: (err: ExtendedError) => void) {
const fns = this._fns.slice(0);
if (!fns.length) return fn(null);

Expand Down Expand Up @@ -129,7 +135,11 @@ export class Namespace extends EventEmitter {
this.run(socket, err => {
process.nextTick(() => {
if ("open" == client.conn.readyState) {
if (err) return socket._error(err.message);
if (err)
return socket._error({
message: err.message,
data: err.data
});

// track socket
this.sockets.set(socket.id, socket);
Expand Down
29 changes: 26 additions & 3 deletions test/socket.io.ts
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@ describe("socket.io", () => {
srv.listen(() => {
const socket = client(srv, "/doesnotexist");
socket.on("connect_error", err => {
expect(err).to.be("Invalid namespace");
expect(err.message).to.be("Invalid namespace");
done();
});
});
Expand Down Expand Up @@ -814,7 +814,7 @@ describe("socket.io", () => {
sio.of(/^\/dynamic-\d+$/);
sio.of((name, query, next) => next(null, "/dynamic-101" === name));
socket.on("connect_error", err => {
expect(err).to.be("Invalid namespace");
expect(err.message).to.be("Invalid namespace");
done();
});
});
Expand Down Expand Up @@ -2185,7 +2185,30 @@ describe("socket.io", () => {
done(new Error("nope"));
});
socket.on("connect_error", err => {
expect(err).to.be("Authentication error");
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");
expect(err.data).to.eql({ a: "b", c: 3 });
done();
});
});
Expand Down

0 comments on commit 54bf4a4

Please sign in to comment.