Skip to content

No way to hook into outbound acknowledgements with middlewares #4882

Open
@NyaStone

Description

@NyaStone

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 #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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions