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