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.
93 lines
3.7 KiB
93 lines
3.7 KiB
9 months ago
|
import logging
|
||
|
|
||
|
from engineio import packet as eio_packet
|
||
|
from . import base_manager
|
||
|
from . import packet
|
||
|
|
||
|
default_logger = logging.getLogger('socketio')
|
||
|
|
||
|
|
||
|
class Manager(base_manager.BaseManager):
|
||
|
"""Manage client connections.
|
||
|
|
||
|
This class keeps track of all the clients and the rooms they are in, to
|
||
|
support the broadcasting of messages. The data used by this class is
|
||
|
stored in a memory structure, making it appropriate only for single process
|
||
|
services. More sophisticated storage backends can be implemented by
|
||
|
subclasses.
|
||
|
"""
|
||
|
def can_disconnect(self, sid, namespace):
|
||
|
return self.is_connected(sid, namespace)
|
||
|
|
||
|
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."""
|
||
|
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]
|
||
|
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:
|
||
|
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)
|
||
|
self.server._send_packet(eio_sid, pkt)
|
||
|
|
||
|
def disconnect(self, sid, namespace, **kwargs):
|
||
|
"""Register a client disconnect from a namespace."""
|
||
|
return self.basic_disconnect(sid, namespace)
|
||
|
|
||
|
def enter_room(self, sid, namespace, room, eio_sid=None):
|
||
|
"""Add a client to a room."""
|
||
|
return self.basic_enter_room(sid, namespace, room, eio_sid=eio_sid)
|
||
|
|
||
|
def leave_room(self, sid, namespace, room):
|
||
|
"""Remove a client from a room."""
|
||
|
return self.basic_leave_room(sid, namespace, room)
|
||
|
|
||
|
def close_room(self, room, namespace):
|
||
|
"""Remove all participants from a room."""
|
||
|
return self.basic_close_room(room, namespace)
|
||
|
|
||
|
def trigger_callback(self, sid, id, data):
|
||
|
"""Invoke an application callback."""
|
||
|
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:
|
||
|
callback(*data)
|