You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
98 lines
3.7 KiB
98 lines
3.7 KiB
import asyncio |
|
|
|
from engineio import packet as eio_packet |
|
from socketio import packet |
|
from .base_manager import BaseManager |
|
|
|
|
|
class AsyncManager(BaseManager): |
|
"""Manage a client list for an asyncio server.""" |
|
async def can_disconnect(self, sid, namespace): |
|
return self.is_connected(sid, namespace) |
|
|
|
async def emit(self, event, data, namespace, room=None, skip_sid=None, |
|
callback=None, **kwargs): |
|
"""Emit a message to a single client, a room, or all the clients |
|
connected to the namespace. |
|
|
|
Note: this method is a coroutine. |
|
""" |
|
if namespace not in self.rooms: |
|
return |
|
if isinstance(data, tuple): |
|
# tuples are expanded to multiple arguments, everything else is |
|
# sent as a single argument |
|
data = list(data) |
|
elif data is not None: |
|
data = [data] |
|
else: |
|
data = [] |
|
if not isinstance(skip_sid, list): |
|
skip_sid = [skip_sid] |
|
tasks = [] |
|
if not callback: |
|
# when callbacks aren't used the packets sent to each recipient are |
|
# identical, so they can be generated once and reused |
|
pkt = self.server.packet_class( |
|
packet.EVENT, namespace=namespace, data=[event] + data) |
|
encoded_packet = pkt.encode() |
|
if not isinstance(encoded_packet, list): |
|
encoded_packet = [encoded_packet] |
|
eio_pkt = [eio_packet.Packet(eio_packet.MESSAGE, p) |
|
for p in encoded_packet] |
|
for sid, eio_sid in self.get_participants(namespace, room): |
|
if sid not in skip_sid: |
|
for p in eio_pkt: |
|
tasks.append(asyncio.create_task( |
|
self.server._send_eio_packet(eio_sid, p))) |
|
else: |
|
# callbacks are used, so each recipient must be sent a packet that |
|
# contains a unique callback id |
|
# note that callbacks when addressing a group of people are |
|
# implemented but not tested or supported |
|
for sid, eio_sid in self.get_participants(namespace, room): |
|
if sid not in skip_sid: # pragma: no branch |
|
id = self._generate_ack_id(sid, callback) |
|
pkt = self.server.packet_class( |
|
packet.EVENT, namespace=namespace, data=[event] + data, |
|
id=id) |
|
tasks.append(asyncio.create_task( |
|
self.server._send_packet(eio_sid, pkt))) |
|
if tasks == []: # pragma: no cover |
|
return |
|
await asyncio.wait(tasks) |
|
|
|
async def disconnect(self, sid, namespace, **kwargs): |
|
"""Disconnect a client. |
|
|
|
Note: this method is a coroutine. |
|
""" |
|
return super().disconnect(sid, namespace, **kwargs) |
|
|
|
async def close_room(self, room, namespace): |
|
"""Remove all participants from a room. |
|
|
|
Note: this method is a coroutine. |
|
""" |
|
return super().close_room(room, namespace) |
|
|
|
async def trigger_callback(self, sid, id, data): |
|
"""Invoke an application callback. |
|
|
|
Note: this method is a coroutine. |
|
""" |
|
callback = None |
|
try: |
|
callback = self.callbacks[sid][id] |
|
except KeyError: |
|
# if we get an unknown callback we just ignore it |
|
self._get_logger().warning('Unknown callback received, ignoring.') |
|
else: |
|
del self.callbacks[sid][id] |
|
if callback is not None: |
|
ret = callback(*data) |
|
if asyncio.iscoroutine(ret): |
|
try: |
|
await ret |
|
except asyncio.CancelledError: # pragma: no cover |
|
pass
|
|
|