socketio / socket.io-admin-ui

Admin UI for Socket.IO

Home Page:https://admin.socket.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Using Admin UI with Acknowledgements breaks with Could not encode error

mzalewski opened this issue · comments

Libraries used:
Socket IO
socket.io-admin-ui
socket.io-redis

When receiving an event with an acknowledgment callback, I'm seeing a 'Could not encode' error being thrown from within socket.io-redis.

It appears to be caused by the additional timestamp being added here:

  socket.onAny((...args) => {
      adminNamespace.emit("event_received", nsp.name, socket.id, args, new Date());
  });

This is then passed through the Socket IO Broadcast Adapter, and then to the Redis Adapter which serialises all the arguments. When it tries to serialise the callback function, it throws an error since the encode library used doesn't support functions.

Without Admin UI installed, this issue is avoided because the Broadcast Adapter correctly identifies the acknowledgement callback. It does this using this with the following code:
const withAck = typeof data[data.length - 1] === "function";

When Admin UI is installed and enabled, the line above doesn't detect the acknowledgement as it is no longer the last argument.

Example test which demonstrates issue:

    describe(`RedisStore with Redis Client`, () => {
      let port: number, io: Server, redisClient: RedisClient;

      beforeEach((done) => {
        const httpServer = createServer();
        io = new Server(httpServer);

        const redisClient = createClient();
        const subClient = redisClient.duplicate();
        io.adapter(
          createAdapter({ pubClient: redisClient, subClient: subClient })
        );
        httpServer.listen(() => {
          port = (httpServer.address() as AddressInfo).port;
          done();
        });
      });

      afterEach(() => {
        io.close();
      });

      it("emits event when event received", async () => {
        instrument(io, {
          auth: false,
        });

        const adminSocket = ioc(`http://localhost:${port}/admin`);

        await waitFor(adminSocket, "connect");

        const clientSocket = ioc(`http://localhost:${port}`, {
          forceNew: true,
          transports: ["polling"],
        });

        io.use((socket, next) => {
          socket.data = socket.data || {};
          socket.data.count = 1;
          socket.data.array = [1];
          next();
        });

        const [socket] = await waitFor(adminSocket, "socket_connected");
        expect(socket.data).to.eql({ count: 1, array: [1] });
        clientSocket.emit("event-name", "test-arg", function () {});

        adminSocket.disconnect();
        clientSocket.disconnect();
      });
    });

This should be fixed by 6d58a75, included in release 0.5.1. Thanks for the detailed report 👍