Skip to content
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

fetchSockets() always returns RemoteSocket[] #4595

Closed
nathan6am opened this issue Jan 15, 2023 · 6 comments
Closed

fetchSockets() always returns RemoteSocket[] #4595

nathan6am opened this issue Jan 15, 2023 · 6 comments
Labels
question Further information is requested
Milestone

Comments

@nathan6am
Copy link

Describe the bug
I am trying to retrieve a socket instance by its id, however, using the io.fetchSockets() method always returns a RemoteSocket[], so methods like socket.timeout can't be used from the resulting instance. According to the docs fetchSockets should return Socket[] | RemoteSocket[]. I am not using an adapter and the behavior is the same in main namespace.

To Reproduce

Please fill the following code example:

Socket.IO server version: 4.5.4

Server

//Returns a Promise<RemoteSocket | undefined>
const socketInstanceById = async (socketId: string) => {
    const sockets = await io.in(socketId).fetchSockets(); 
    for (const socket of sockets) {
      if (socket.id === socketId) return socket;
    }
 }

Reducing the example to

const sockets = await io.fetchSockets()
//Type of `sockets` is RemoteSocket[]

Is fetchSockets the only way to retrieve a socket instance by its id? If so is there a way to get a Socket instance and not a RemoteSocket?

Expected behavior
fetchSockets should return normal socket instances when called on the same server.

@nathan6am nathan6am added the to triage Waiting to be triaged by a member of the team label Jan 15, 2023
@nathan6am nathan6am changed the title fetchSockets() always returns RemoteSocket[] fetchSockets() always returns RemoteSocket[] Jan 15, 2023
@darrachequesne
Copy link
Member

Getting a RemoteSocket means the client is connected on another server of the cluster.

Regarding the timeout() method, it's a known issue that should be fixed soon: #4558

@darrachequesne darrachequesne added question Further information is requested and removed to triage Waiting to be triaged by a member of the team labels Jan 16, 2023
@nathan6am
Copy link
Author

nathan6am commented Jan 16, 2023

Why would I get a RemoteSocket if I am only using a single server with no clustering? As far as I can tell, fetchSockets() will always return RemoteSocket[] regardless of whether or not the client is connected to another server on the cluster, including without using clustering at all, while according to the docs it should return either Socket[] | RemoteSocket[]

public fetchSockets(): Promise<RemoteSocket<EmitEvents, SocketData>[]> {
return this.adapter
.fetchSockets({
rooms: this.rooms,
except: this.exceptRooms,
flags: this.flags,
})
.then((sockets) => {
return sockets.map((socket) => {
if (socket instanceof Socket) {
// FIXME the TypeScript compiler complains about missing private properties
return socket as unknown as RemoteSocket<EmitEvents, SocketData>;
} else {
return new RemoteSocket(
this.adapter,
socket as SocketDetails<SocketData>
);
}
});
});
}

@darrachequesne
Copy link
Member

The type of the fetchSockets() response is RemoteSocket[], but it might actually contain some Socket instances (for sockets that are on the given server), which share more or less the same interface:

if (socket instanceof Socket) {
// FIXME the TypeScript compiler complains about missing private properties
return socket as unknown as RemoteSocket<EmitEvents, SocketData>;

Is there anything that should be exposed by the RemoteSocket interface?

darrachequesne pushed a commit that referenced this issue Jan 24, 2023
The RemoteSocket interface, which is returned when the client is
connected on another Socket.IO server of the cluster, was lacking the
`timeout()` method.

Syntax:

```js
const sockets = await io.fetchSockets();

for (const socket of sockets) {
  if (someCondition) {
    socket.timeout(1000).emit("some-event", (err) => {
      if (err) {
        // the client did not acknowledge the event in the given delay
      }
    });
  }
}
```

Related: #4595
@darrachequesne
Copy link
Member

For future readers:

remoteSocket.timeout() was added in 0c0eb00, included in version 4.6.0.

Please reopen if needed!

@darrachequesne darrachequesne added this to the 4.6.0 milestone Feb 17, 2023
haneenmahd pushed a commit to haneenmahd/socket.io that referenced this issue Apr 15, 2023
The RemoteSocket interface, which is returned when the client is
connected on another Socket.IO server of the cluster, was lacking the
`timeout()` method.

Syntax:

```js
const sockets = await io.fetchSockets();

for (const socket of sockets) {
  if (someCondition) {
    socket.timeout(1000).emit("some-event", (err) => {
      if (err) {
        // the client did not acknowledge the event in the given delay
      }
    });
  }
}
```

Related: socketio#4595
dzad pushed a commit to dzad/socket.io that referenced this issue May 29, 2023
The RemoteSocket interface, which is returned when the client is
connected on another Socket.IO server of the cluster, was lacking the
`timeout()` method.

Syntax:

```js
const sockets = await io.fetchSockets();

for (const socket of sockets) {
  if (someCondition) {
    socket.timeout(1000).emit("some-event", (err) => {
      if (err) {
        // the client did not acknowledge the event in the given delay
      }
    });
  }
}
```

Related: socketio#4595
@TonyLst
Copy link

TonyLst commented Jan 9, 2024

Getting a RemoteSocket means the client is connected on another server of the cluster.

Regarding the timeout() method, it's a known issue that should be fixed soon: #4558

Is there a way to get complete Socket client instance instead of RemoteSocket when using fetchSockets() in clusterized app?

I would like to access socket.request.user property (user prop is provided by my express-session/passportjs middlewares) juste as i was able to access when my app was not clusterized:

const activeParticipants: Array<Participant> = (
            await browserSocket.adapter.fetchSockets({
                rooms: new Set([code]),
            })
        )
            .map(
                (socket) =>
                    (socket as unknown as BrowserSocket).request.user
            )
            .filter((user) => !isUserAdmin(user));

RemoteClient actually not includes socket.request 😭

@darrachequesne
Copy link
Member

Hi! The request object is a Node.js IncomingMessage message, and cannot be serialized.

You can include the user's id in the socket.data attribute:

io.on("connection", (socket) => {
  socket.data.userId = socket.request.user.id;
});

And then use it with fetchSockets():

const sockets = await io.fetchSockets();

for (const socket of sockets) {
  console.log(socket.data.userId);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants