socketio / socketio/socket.io

No way to hook into outbound acknowledgements with middlewares

Open
#4,882 0 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
TypeScript
Stars
63.2k
Forks
10.3k
Avg merge
11d 20h
Merged PRs (30d)
2

Description

I've been working on some middleware for data validation and infering the event types using a zod schema.
When looking for how to handle acknowledgements through my middleware I found a few unexpected behavors of catch-all listener.

It is possible to add some logic before acknowledging a client's request.

socket.use(packet: [string, ...any[]], next:(err?: Error) => void) {
    console.log(packet);
    // [ 'eventWithAck', [Function (anonymous)] ]
    next();
};

socket.on('eventWithAck', (ack: () => void) => {
    ack();
});

client.emit('eventWithAck', () => {
    console.log('done');
});

Here we can get access to the callback that is responding to the client, and we can wrap it with our own logic to be run right before the acknowledgement is sent.

socket.use(packet: [string, ...any[]], next:(err?: Error) => void) {
    if (packet.length && typeof packet[packet.length-1] === 'function') {
        const callback: (...args: any[]) => void = packet[packet.length-1];
        packet[packet.length-1] = (...args: any[]) => {
            console.log(args);
            // [ 'some data' ]
            callback(...args);
        }
    }
    next();
};

socket.on('eventWithAck', (ack: (data: string) => void) => {
    ack('some data');
});

client.emit('eventWithAck', () => {
    console.log('done');
});

The issue is that this is only achievable to check acknledgement that we send out, not the incomming acknowlegements data packets.
I had expected to achieve a similar logic using the catch-all listeners.
The documentation on the Socket.io website stated that catch all listeners don't trigger upon recieving/sending acknoledgements, but i hoped being able to get a reference to the acknowledgement callback before the emit is being sent, so i could wrap it with my logic. Wrapping the acknowledgement callback there allows me to also have context of the initial emited event (before it's sent) for tha added logic.
However when I tried i quickly noticed that the acknowledgement callback gets stripped away from the data packet and sent to the acknowledgement listeners before the catch-all listeners get called.
Second issue with this proposition is that I don't know if the spread arguments would pass the callback by reference so it can be wrapped.

While writing this I noticed that the documentation in VS code of catch all listeners does warn me about the callback being stripped.

The solution proposed here https://github.com/socketio/socket.io/issues/4649 seems like migration hell because of potential collisions when suddenly triggering the listeners for new reasons.

Changing the definition of the catch-all listeners to have the callback being passed as reference would be a real migration pain too. Even tho it would be a good way of having the context of the event that originates the acknowledgement.

Best option I could see would be a method to add middleware support for outgoing requests among these lines :

public useOutgoing(eventPacket: any[], next: (err?: Error) => void) {
    // the packet being passed as reference allows us to manipulate it and wrap the acknowledgement callback with validation logic
}

Currently working on a fork to demonstrate. (just have to add some more test cases)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the existing socket.use catch-all behavior described here and the fork mentioned in the issue; no repository file or test is named. Compare the proposed useOutgoing entry point with incoming acknowledgement handling, and define completion as a reviewed API that exposes the outbound packet and acknowledgement context without breaking existing listeners.

Written by the indexing model from the issue text.

Assessment

Tech stack
node.js, typescript
Domain
api, backend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.