Skip to content

Commit

Permalink
revert: restore the socket middleware functionality
Browse files Browse the repository at this point in the history
This functionality was removed in [1] (included in 3.0.0), but
catch-all listeners and socket middleware features are complementary
rather than mutually exclusive.

The only difference with the previous implementation is that passing an
error to the `next` handler will create an error on the server-side,
and not on the client-side anymore.

```js
io.on("connection", (socket) => {

  socket.use(([ event, ...args ], next) => {
    next(new Error("stop"));
  });

  socket.on("error", (err) => {
    // to restore the previous behavior
    socket.emit("error", err);

    // or close the connection, depending on your use case
    socket.disconnect(true);
  });
});
```

This creates additional possibilities about custom error handlers, which
may be implemented in the future.

```js
// user-defined error handler
socket.use((err, [ event ], next) => {
  // either handle it
  socket.disconnect();

  // or forward the error to the default error handler
  next(err);
});

// default error handler
socket.use((err, _, next) => {
  socket.emit("error", err);
});
```

Related: #3678

[1]: 5c73733
  • Loading branch information
darrachequesne committed Jan 5, 2021
1 parent 170b739 commit bf54327
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 1 deletion.
61 changes: 60 additions & 1 deletion lib/socket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ export class Socket extends EventEmitter {
listener.apply(this, args);
}
}
super.emit.apply(this, args);
this.dispatch(args);
}

/**
Expand Down Expand Up @@ -511,6 +511,65 @@ export class Socket extends EventEmitter {
return this;
}

/**
* Dispatch incoming event to socket listeners.
*
* @param {Array} event - event that will get emitted
* @private
*/
private dispatch(event): void {
debug("dispatching an event %j", event);
this.run(event, (err) => {
process.nextTick(() => {
if (err) {
return this._onerror(err);
}
super.emit.apply(this, event);
});
});
}

/**
* Sets up socket middleware.
*
* @param {Function} fn - middleware function (event, next)
* @return {Socket} self
* @public
*/
public use(
fn: (event: Array<any>, next: (err: Error) => void) => void
): Socket {
this.fns.push(fn);
return this;
}

/**
* Executes the middleware for an incoming event.
*
* @param {Array} event - event that will get emitted
* @param {Function} fn - last fn call in the middleware
* @private
*/
private run(event: Array<any>, fn: (err: Error | null) => void) {
const fns = this.fns.slice(0);
if (!fns.length) return fn(null);

function run(i) {
fns[i](event, function (err) {
// upon error, short-circuit
if (err) return fn(err);

// if no middleware left, summon callback
if (!fns[i + 1]) return fn(null);

// go on to next
run(i + 1);
});
}

run(0);
}

/**
* A reference to the request that originated the underlying Engine.IO Socket.
*
Expand Down
71 changes: 71 additions & 0 deletions test/socket.io.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type { AddressInfo } from "net";
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 Down Expand Up @@ -2359,4 +2360,74 @@ describe("socket.io", () => {
});
});
});

describe("socket middleware", () => {
const { Socket } = require("../dist/socket");

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();
});
});
});
});
});
});

0 comments on commit bf54327

Please sign in to comment.