-
Notifications
You must be signed in to change notification settings - Fork 10.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Using await within emit causes payload to be sent to all connected clients #3431
Comments
Sometime my app has issue :
What can I do to resolve this problem ? |
Hey, I see two possible problems with your code:
To solve this problem you'll simply need to define/grab strangerSocket1 somewhere :). |
Sorry, I have some mistake when upload my code . I have been update it . My issue |
I'm assuming |
Thank you ! I think it's a best answer ! |
@tommoor The problem isn't with In your example: [1, 2, 3].forEach(async () => {
console.log("sending to private room");
io.to('my-private-room').emit('event', {
data: await sleep(100)
});
}); The call order is: io.to(roomId) // rooms: [] -> [roomId]
io.to(roomId) // rooms: [roomId] -> [roomId]
io.to(roomId) // rooms: [roomId] -> [roomId]
io.emit(..) // rooms: [roomId] -> [] Promise resolved
io.emit(..) // rooms: [] -> [] Promise resolved
io.emit(..) // rooms: [] -> [] Promise resolved After the first Promise resolves, The simple fix for this is to move the [1, 2, 3].forEach(async () => {
console.log("sending to private room");
let data = await sleep(100);
io.to('my-private-room').emit('event', {
data
});
}); This yields a call order of: io.to(roomId) // rooms: [] -> [roomId] Promise resolved
io.emit(..) // rooms: [roomId] -> []
io.to(roomId) // rooms: [] -> [roomId] Promise resolved
io.emit(..) // rooms: [roomId] -> []
io.to(roomId) // rooms: [] -> [roomId] Promise resolved
io.emit(..) // rooms: [roomId] -> [] Hope this helps |
Yep, this seems right and how we resolved it. The bug is really bad though and leaked production data to the wrong user in my circumstance so felt it worth filing as a warning to others |
@tommoor right there with you. Leaked data to wrong client. Yikes. |
To get around this for my case, I store socket object in-memory in a map of immutablejs with the roomID as they key. And then I emit directly to the sockets. |
So absolutely no chance of fixing this? Are we stuck on |
@lifenautjoe Not sure of your exact situation or how your promise chain would look any different considering this issue. But I'm not sure this is something that needs to be "fixed". I think it's just something that requires some special consideration / documentation. You just have to be careful to resolve your promise before making any calls to |
The behavior is indeed quite surprising and should be fixed IMO. I'm not sure if we could fix this in a backward-compatible way though. Until then, I added a note here: https://socket.io/docs/v3/rooms/#Usage-with-asynchronous-code |
I ran into this issue as well. At the very least, I think the documentation of |
Previously, broadcasting to a given room (by calling `io.to()`) would mutate the io instance, which could lead to surprising behaviors, like: ```js io.to("room1"); io.to("room2").emit(...); // also sent to room1 // or with async/await io.to("room3").emit("details", await fetchDetails()); // random behavior: maybe in room3, maybe to all clients ``` Calling `io.to()` (or any other broadcast modifier) will now return an immutable instance. Related: - #3431 - #3444
@darrachequesne I saw your fix for this issue with ac9e8ca and just wanted to mention that, as you already pointed out yourself, it is indeed not backwards compatible. In a codebase of mine I used a fragment like this: for (let id of ids) {
io.to(id);
}
io.emit('some', 'message'); which will break. I can imagine that I'm not the only one doing something like this, so I think this has to go into a semver major. |
This was fixed by ac9e8ca and included in Calling const operator1 = io.to("room1");
const operator2 = operator1.to("room2");
const operator3 = socket.broadcast;
const operator4 = socket.to("room3").to("room4");
operator1.emit(/* ... */); // only to clients in "room1"
operator2.emit(/* ... */); // to clients in "room1" or in "room2"
operator3.emit(/* ... */); // to all clients but the sender
operator4.emit(/* ... */); // to clients in "room3" or in "room4" but the sender Documentation: https://socket.io/docs/v3/migrating-from-3-x-to-4-0/#io-to-is-now-immutable |
I'm here because there is apparently a big bug but after reading I see absolutely no bug at all What I do see is a very big misunderstanding of a fundamental and simple JavaScript evaluation order. If that evaluation order is taken into consideration the perceived bug turns out to live in the implementer code not socket.io To be concise, we are talking about 'method chaining'. i.e.
The example provided and discussed is
I understand many who claim there is a bug have a perceived race-condition, and having Maybe they could remove the ability for chaining? That would make the evaluation order of chaining a non-issue - but it seems so obvious (chaining evaluation is basic JavaScript syntax) that I am surprised this issue exists. I really did not mean this to read like a mad burn to the people who claim there is a bug, I really genuinely intend this explanation to educate and help readers to see that there is no bug here at all. Tip: you don't understand chaining yet? or the evaluation order confuses you, then it is a good idea to simply avoid chaining and the so-called EDIT: the change in |
@chrisdlangton surely from your example |
@Overdash you're still assuming the order is (1) This new approach in version 4.0.0 will not return the object instance any more, instead it returns a new immutable object copied from the original effectively forking it. The evaluation is still not (1) When you see that there is an You see now? |
@chrisdlangton your comments don't read like a "mad burn", but they are extremely condescending. There was a bug, and it's been resolved from what I can tell – your input isn't particularly helpful. |
@tommoor simply put, evaluation order of chaining and their inputs is a fundamental JavaScript concept. Learning this and reasoning with it for any code or library is a basicc skill. The value i provided in explaining it correctly and giving clarity that it's not a 'bug' but perhaps a misunderstanding of the language that can apply in any JavaScript situation that utilise standard chaining (that returns |
You want to:
Current behaviour
Using
async/await
to construct payloads results in a race condition and messages potentially being broadcast to all connections.Steps to reproduce
Fiddle: https://github.com/tommoor/socket.io-fiddle/tree/async-await-critical-issue
TLDR: Using
await
within the payload construction foremit
will cause the message to be sent to all connections instead of the specific room. eg:Expected behaviour
Data should be sent to the correct room and not leaked to all clients.
Setup
The text was updated successfully, but these errors were encountered: