|
- from functools import wraps
- import os
- import sys
-
- # make sure gevent-socketio is not installed, as it conflicts with
- # python-socketio
- gevent_socketio_found = True
- try:
- from socketio import socketio_manage
- except ImportError:
- gevent_socketio_found = False
- if gevent_socketio_found:
- print('The gevent-socketio package is incompatible with this version of '
- 'the Flask-SocketIO extension. Please uninstall it, and then '
- 'install the latest version of python-socketio in its place.')
- sys.exit(1)
-
- import flask
- from flask import _request_ctx_stack, json as flask_json
- from flask.sessions import SessionMixin
- import socketio
- from socketio.exceptions import ConnectionRefusedError
- from werkzeug.debug import DebuggedApplication
- from werkzeug.serving import run_with_reloader
-
- from .namespace import Namespace
- from .test_client import SocketIOTestClient
-
- __version__ = '4.2.1'
-
-
- class _SocketIOMiddleware(socketio.WSGIApp):
- """This WSGI middleware simply exposes the Flask application in the WSGI
- environment before executing the request.
- """
- def __init__(self, socketio_app, flask_app, socketio_path='socket.io'):
- self.flask_app = flask_app
- super(_SocketIOMiddleware, self).__init__(socketio_app,
- flask_app.wsgi_app,
- socketio_path=socketio_path)
-
- def __call__(self, environ, start_response):
- environ = environ.copy()
- environ['flask.app'] = self.flask_app
- return super(_SocketIOMiddleware, self).__call__(environ,
- start_response)
-
-
- class _ManagedSession(dict, SessionMixin):
- """This class is used for user sessions that are managed by
- Flask-SocketIO. It is simple dict, expanded with the Flask session
- attributes."""
- pass
-
-
- class SocketIO(object):
- """Create a Flask-SocketIO server.
-
- :param app: The flask application instance. If the application instance
- isn't known at the time this class is instantiated, then call
- ``socketio.init_app(app)`` once the application instance is
- available.
- :param manage_session: If set to ``True``, this extension manages the user
- session for Socket.IO events. If set to ``False``,
- Flask's own session management is used. When using
- Flask's cookie based sessions it is recommended that
- you leave this set to the default of ``True``. When
- using server-side sessions, a ``False`` setting
- enables sharing the user session between HTTP routes
- and Socket.IO events.
- :param message_queue: A connection URL for a message queue service the
- server can use for multi-process communication. A
- message queue is not required when using a single
- server process.
- :param channel: The channel name, when using a message queue. If a channel
- isn't specified, a default channel will be used. If
- multiple clusters of SocketIO processes need to use the
- same message queue without interfering with each other, then
- each cluster should use a different channel.
- :param path: The path where the Socket.IO server is exposed. Defaults to
- ``'socket.io'``. Leave this as is unless you know what you are
- doing.
- :param resource: Alias to ``path``.
- :param kwargs: Socket.IO and Engine.IO server options.
-
- The Socket.IO server options are detailed below:
-
- :param client_manager: The client manager instance that will manage the
- client list. When this is omitted, the client list
- is stored in an in-memory structure, so the use of
- multiple connected servers is not possible. In most
- cases, this argument does not need to be set
- explicitly.
- :param logger: To enable logging set to ``True`` or pass a logger object to
- use. To disable logging set to ``False``. The default is
- ``False``.
- :param binary: ``True`` to support binary payloads, ``False`` to treat all
- payloads as text. On Python 2, if this is set to ``True``,
- ``unicode`` values are treated as text, and ``str`` and
- ``bytes`` values are treated as binary. This option has no
- effect on Python 3, where text and binary payloads are
- always automatically discovered.
- :param json: An alternative json module to use for encoding and decoding
- packets. Custom json modules must have ``dumps`` and ``loads``
- functions that are compatible with the standard library
- versions. To use the same json encoder and decoder as a Flask
- application, use ``flask.json``.
- :param async_handlers: If set to ``True``, event handlers for a client are
- executed in separate threads. To run handlers for a
- client synchronously, set to ``False``. The default
- is ``True``.
- :param always_connect: When set to ``False``, new connections are
- provisory until the connect handler returns
- something other than ``False``, at which point they
- are accepted. When set to ``True``, connections are
- immediately accepted, and then if the connect
- handler returns ``False`` a disconnect is issued.
- Set to ``True`` if you need to emit events from the
- connect handler and your client is confused when it
- receives events before the connection acceptance.
- In any other case use the default of ``False``.
-
- The Engine.IO server configuration supports the following settings:
-
- :param async_mode: The asynchronous model to use. See the Deployment
- section in the documentation for a description of the
- available options. Valid async modes are
- ``threading``, ``eventlet``, ``gevent`` and
- ``gevent_uwsgi``. If this argument is not given,
- ``eventlet`` is tried first, then ``gevent_uwsgi``,
- then ``gevent``, and finally ``threading``. The
- first async mode that has all its dependencies installed
- is then one that is chosen.
- :param ping_timeout: The time in seconds that the client waits for the
- server to respond before disconnecting. The default is
- 60 seconds.
- :param ping_interval: The interval in seconds at which the client pings
- the server. The default is 25 seconds.
- :param max_http_buffer_size: The maximum size of a message when using the
- polling transport. The default is 100,000,000
- bytes.
- :param allow_upgrades: Whether to allow transport upgrades or not. The
- default is ``True``.
- :param http_compression: Whether to compress packages when using the
- polling transport. The default is ``True``.
- :param compression_threshold: Only compress messages when their byte size
- is greater than this value. The default is
- 1024 bytes.
- :param cookie: Name of the HTTP cookie that contains the client session
- id. If set to ``None``, a cookie is not sent to the client.
- The default is ``'io'``.
- :param cors_allowed_origins: Origin or list of origins that are allowed to
- connect to this server. Only the same origin
- is allowed by default. Set this argument to
- ``'*'`` to allow all origins, or to ``[]`` to
- disable CORS handling.
- :param cors_credentials: Whether credentials (cookies, authentication) are
- allowed in requests to this server. The default is
- ``True``.
- :param monitor_clients: If set to ``True``, a background task will ensure
- inactive clients are closed. Set to ``False`` to
- disable the monitoring task (not recommended). The
- default is ``True``.
- :param engineio_logger: To enable Engine.IO logging set to ``True`` or pass
- a logger object to use. To disable logging set to
- ``False``. The default is ``False``.
- """
-
- def __init__(self, app=None, **kwargs):
- self.server = None
- self.server_options = {}
- self.wsgi_server = None
- self.handlers = []
- self.namespace_handlers = []
- self.exception_handlers = {}
- self.default_exception_handler = None
- self.manage_session = True
- # We can call init_app when:
- # - we were given the Flask app instance (standard initialization)
- # - we were not given the app, but we were given a message_queue
- # (standard initialization for auxiliary process)
- # In all other cases we collect the arguments and assume the client
- # will call init_app from an app factory function.
- if app is not None or 'message_queue' in kwargs:
- self.init_app(app, **kwargs)
- else:
- self.server_options.update(kwargs)
-
- def init_app(self, app, **kwargs):
- if app is not None:
- if not hasattr(app, 'extensions'):
- app.extensions = {} # pragma: no cover
- app.extensions['socketio'] = self
- self.server_options.update(kwargs)
- self.manage_session = self.server_options.pop('manage_session',
- self.manage_session)
-
- if 'client_manager' not in self.server_options:
- url = self.server_options.pop('message_queue', None)
- channel = self.server_options.pop('channel', 'flask-socketio')
- write_only = app is None
- if url:
- if url.startswith(('redis://', "rediss://")):
- queue_class = socketio.RedisManager
- elif url.startswith(('kafka://')):
- queue_class = socketio.KafkaManager
- elif url.startswith('zmq'):
- queue_class = socketio.ZmqManager
- else:
- queue_class = socketio.KombuManager
- queue = queue_class(url, channel=channel,
- write_only=write_only)
- self.server_options['client_manager'] = queue
-
- if 'json' in self.server_options and \
- self.server_options['json'] == flask_json:
- # flask's json module is tricky to use because its output
- # changes when it is invoked inside or outside the app context
- # so here to prevent any ambiguities we replace it with wrappers
- # that ensure that the app context is always present
- class FlaskSafeJSON(object):
- @staticmethod
- def dumps(*args, **kwargs):
- with app.app_context():
- return flask_json.dumps(*args, **kwargs)
-
- @staticmethod
- def loads(*args, **kwargs):
- with app.app_context():
- return flask_json.loads(*args, **kwargs)
-
- self.server_options['json'] = FlaskSafeJSON
-
- resource = self.server_options.pop('path', None) or \
- self.server_options.pop('resource', None) or 'socket.io'
- if resource.startswith('/'):
- resource = resource[1:]
- if os.environ.get('FLASK_RUN_FROM_CLI'):
- if self.server_options.get('async_mode') is None:
- if app is not None:
- app.logger.warning(
- 'Flask-SocketIO is Running under Werkzeug, WebSocket '
- 'is not available.')
- self.server_options['async_mode'] = 'threading'
- self.server = socketio.Server(**self.server_options)
- self.async_mode = self.server.async_mode
- for handler in self.handlers:
- self.server.on(handler[0], handler[1], namespace=handler[2])
- for namespace_handler in self.namespace_handlers:
- self.server.register_namespace(namespace_handler)
-
- if app is not None:
- # here we attach the SocketIO middlware to the SocketIO object so it
- # can be referenced later if debug middleware needs to be inserted
- self.sockio_mw = _SocketIOMiddleware(self.server, app,
- socketio_path=resource)
- app.wsgi_app = self.sockio_mw
-
- def on(self, message, namespace=None):
- """Decorator to register a SocketIO event handler.
-
- This decorator must be applied to SocketIO event handlers. Example::
-
- @socketio.on('my event', namespace='/chat')
- def handle_my_custom_event(json):
- print('received json: ' + str(json))
-
- :param message: The name of the event. This is normally a user defined
- string, but a few event names are already defined. Use
- ``'message'`` to define a handler that takes a string
- payload, ``'json'`` to define a handler that takes a
- JSON blob payload, ``'connect'`` or ``'disconnect'``
- to create handlers for connection and disconnection
- events.
- :param namespace: The namespace on which the handler is to be
- registered. Defaults to the global namespace.
- """
- namespace = namespace or '/'
-
- def decorator(handler):
- @wraps(handler)
- def _handler(sid, *args):
- return self._handle_event(handler, message, namespace, sid,
- *args)
-
- if self.server:
- self.server.on(message, _handler, namespace=namespace)
- else:
- self.handlers.append((message, _handler, namespace))
- return handler
- return decorator
-
- def on_error(self, namespace=None):
- """Decorator to define a custom error handler for SocketIO events.
-
- This decorator can be applied to a function that acts as an error
- handler for a namespace. This handler will be invoked when a SocketIO
- event handler raises an exception. The handler function must accept one
- argument, which is the exception raised. Example::
-
- @socketio.on_error(namespace='/chat')
- def chat_error_handler(e):
- print('An error has occurred: ' + str(e))
-
- :param namespace: The namespace for which to register the error
- handler. Defaults to the global namespace.
- """
- namespace = namespace or '/'
-
- def decorator(exception_handler):
- if not callable(exception_handler):
- raise ValueError('exception_handler must be callable')
- self.exception_handlers[namespace] = exception_handler
- return exception_handler
- return decorator
-
- def on_error_default(self, exception_handler):
- """Decorator to define a default error handler for SocketIO events.
-
- This decorator can be applied to a function that acts as a default
- error handler for any namespaces that do not have a specific handler.
- Example::
-
- @socketio.on_error_default
- def error_handler(e):
- print('An error has occurred: ' + str(e))
- """
- if not callable(exception_handler):
- raise ValueError('exception_handler must be callable')
- self.default_exception_handler = exception_handler
- return exception_handler
-
- def on_event(self, message, handler, namespace=None):
- """Register a SocketIO event handler.
-
- ``on_event`` is the non-decorator version of ``'on'``.
-
- Example::
-
- def on_foo_event(json):
- print('received json: ' + str(json))
-
- socketio.on_event('my event', on_foo_event, namespace='/chat')
-
- :param message: The name of the event. This is normally a user defined
- string, but a few event names are already defined. Use
- ``'message'`` to define a handler that takes a string
- payload, ``'json'`` to define a handler that takes a
- JSON blob payload, ``'connect'`` or ``'disconnect'``
- to create handlers for connection and disconnection
- events.
- :param handler: The function that handles the event.
- :param namespace: The namespace on which the handler is to be
- registered. Defaults to the global namespace.
- """
- self.on(message, namespace=namespace)(handler)
-
- def on_namespace(self, namespace_handler):
- if not isinstance(namespace_handler, Namespace):
- raise ValueError('Not a namespace instance.')
- namespace_handler._set_socketio(self)
- if self.server:
- self.server.register_namespace(namespace_handler)
- else:
- self.namespace_handlers.append(namespace_handler)
-
- def emit(self, event, *args, **kwargs):
- """Emit a server generated SocketIO event.
-
- This function emits a SocketIO event to one or more connected clients.
- A JSON blob can be attached to the event as payload. This function can
- be used outside of a SocketIO event context, so it is appropriate to
- use when the server is the originator of an event, outside of any
- client context, such as in a regular HTTP request handler or a
- background task. Example::
-
- @app.route('/ping')
- def ping():
- socketio.emit('ping event', {'data': 42}, namespace='/chat')
-
- :param event: The name of the user event to emit.
- :param args: A dictionary with the JSON data to send as payload.
- :param namespace: The namespace under which the message is to be sent.
- Defaults to the global namespace.
- :param room: Send the message to all the users in the given room. If
- this parameter is not included, the event is sent to
- all connected users.
- :param skip_sid: The session id of a client to ignore when broadcasting
- or addressing a room. This is typically set to the
- originator of the message, so that everyone except
- that client receive the message. To skip multiple sids
- pass a list.
- :param callback: If given, this function will be called to acknowledge
- that the client has received the message. The
- arguments that will be passed to the function are
- those provided by the client. Callback functions can
- only be used when addressing an individual client.
- """
- namespace = kwargs.pop('namespace', '/')
- room = kwargs.pop('room', None)
- include_self = kwargs.pop('include_self', True)
- skip_sid = kwargs.pop('skip_sid', None)
- if not include_self and not skip_sid:
- skip_sid = flask.request.sid
- callback = kwargs.pop('callback', None)
- if callback:
- # wrap the callback so that it sets app app and request contexts
- sid = flask.request.sid
- original_callback = callback
-
- def _callback_wrapper(*args):
- return self._handle_event(original_callback, None, namespace,
- sid, *args)
-
- callback = _callback_wrapper
- self.server.emit(event, *args, namespace=namespace, room=room,
- skip_sid=skip_sid, callback=callback, **kwargs)
-
- def send(self, data, json=False, namespace=None, room=None,
- callback=None, include_self=True, skip_sid=None, **kwargs):
- """Send a server-generated SocketIO message.
-
- This function sends a simple SocketIO message to one or more connected
- clients. The message can be a string or a JSON blob. This is a simpler
- version of ``emit()``, which should be preferred. This function can be
- used outside of a SocketIO event context, so it is appropriate to use
- when the server is the originator of an event.
-
- :param data: The message to send, either a string or a JSON blob.
- :param json: ``True`` if ``message`` is a JSON blob, ``False``
- otherwise.
- :param namespace: The namespace under which the message is to be sent.
- Defaults to the global namespace.
- :param room: Send the message only to the users in the given room. If
- this parameter is not included, the message is sent to
- all connected users.
- :param skip_sid: The session id of a client to ignore when broadcasting
- or addressing a room. This is typically set to the
- originator of the message, so that everyone except
- that client receive the message. To skip multiple sids
- pass a list.
- :param callback: If given, this function will be called to acknowledge
- that the client has received the message. The
- arguments that will be passed to the function are
- those provided by the client. Callback functions can
- only be used when addressing an individual client.
- """
- skip_sid = flask.request.sid if not include_self else skip_sid
- if json:
- self.emit('json', data, namespace=namespace, room=room,
- skip_sid=skip_sid, callback=callback, **kwargs)
- else:
- self.emit('message', data, namespace=namespace, room=room,
- skip_sid=skip_sid, callback=callback, **kwargs)
-
- def close_room(self, room, namespace=None):
- """Close a room.
-
- This function removes any users that are in the given room and then
- deletes the room from the server. This function can be used outside
- of a SocketIO event context.
-
- :param room: The name of the room to close.
- :param namespace: The namespace under which the room exists. Defaults
- to the global namespace.
- """
- self.server.close_room(room, namespace)
-
- def run(self, app, host=None, port=None, **kwargs):
- """Run the SocketIO web server.
-
- :param app: The Flask application instance.
- :param host: The hostname or IP address for the server to listen on.
- Defaults to 127.0.0.1.
- :param port: The port number for the server to listen on. Defaults to
- 5000.
- :param debug: ``True`` to start the server in debug mode, ``False`` to
- start in normal mode.
- :param use_reloader: ``True`` to enable the Flask reloader, ``False``
- to disable it.
- :param extra_files: A list of additional files that the Flask
- reloader should watch. Defaults to ``None``
- :param log_output: If ``True``, the server logs all incomming
- connections. If ``False`` logging is disabled.
- Defaults to ``True`` in debug mode, ``False``
- in normal mode. Unused when the threading async
- mode is used.
- :param kwargs: Additional web server options. The web server options
- are specific to the server used in each of the supported
- async modes. Note that options provided here will
- not be seen when using an external web server such
- as gunicorn, since this method is not called in that
- case.
- """
- if host is None:
- host = '127.0.0.1'
- if port is None:
- server_name = app.config['SERVER_NAME']
- if server_name and ':' in server_name:
- port = int(server_name.rsplit(':', 1)[1])
- else:
- port = 5000
-
- debug = kwargs.pop('debug', app.debug)
- log_output = kwargs.pop('log_output', debug)
- use_reloader = kwargs.pop('use_reloader', debug)
- extra_files = kwargs.pop('extra_files', None)
-
- app.debug = debug
- if app.debug and self.server.eio.async_mode != 'threading':
- # put the debug middleware between the SocketIO middleware
- # and the Flask application instance
- #
- # mw1 mw2 mw3 Flask app
- # o ---- o ---- o ---- o
- # /
- # o Flask-SocketIO
- # \ middleware
- # o
- # Flask-SocketIO WebSocket handler
- #
- # BECOMES
- #
- # dbg-mw mw1 mw2 mw3 Flask app
- # o ---- o ---- o ---- o ---- o
- # /
- # o Flask-SocketIO
- # \ middleware
- # o
- # Flask-SocketIO WebSocket handler
- #
- self.sockio_mw.wsgi_app = DebuggedApplication(self.sockio_mw.wsgi_app,
- evalex=True)
-
- if self.server.eio.async_mode == 'threading':
- from werkzeug._internal import _log
- _log('warning', 'WebSocket transport not available. Install '
- 'eventlet or gevent and gevent-websocket for '
- 'improved performance.')
- app.run(host=host, port=port, threaded=True,
- use_reloader=use_reloader, **kwargs)
- elif self.server.eio.async_mode == 'eventlet':
- def run_server():
- import eventlet
- import eventlet.wsgi
- import eventlet.green
- addresses = eventlet.green.socket.getaddrinfo(host, port)
- if not addresses:
- raise RuntimeError('Could not resolve host to a valid address')
- eventlet_socket = eventlet.listen(addresses[0][4], addresses[0][0])
-
- # If provided an SSL argument, use an SSL socket
- ssl_args = ['keyfile', 'certfile', 'server_side', 'cert_reqs',
- 'ssl_version', 'ca_certs',
- 'do_handshake_on_connect', 'suppress_ragged_eofs',
- 'ciphers']
- ssl_params = {k: kwargs[k] for k in kwargs if k in ssl_args}
- if len(ssl_params) > 0:
- for k in ssl_params:
- kwargs.pop(k)
- ssl_params['server_side'] = True # Listening requires true
- eventlet_socket = eventlet.wrap_ssl(eventlet_socket,
- **ssl_params)
-
- eventlet.wsgi.server(eventlet_socket, app,
- log_output=log_output, **kwargs)
-
- if use_reloader:
- run_with_reloader(run_server, extra_files=extra_files)
- else:
- run_server()
- elif self.server.eio.async_mode == 'gevent':
- from gevent import pywsgi
- try:
- from geventwebsocket.handler import WebSocketHandler
- websocket = True
- except ImportError:
- websocket = False
-
- log = 'default'
- if not log_output:
- log = None
- if websocket:
- self.wsgi_server = pywsgi.WSGIServer(
- (host, port), app, handler_class=WebSocketHandler,
- log=log, **kwargs)
- else:
- self.wsgi_server = pywsgi.WSGIServer((host, port), app,
- log=log, **kwargs)
-
- if use_reloader:
- # monkey patching is required by the reloader
- from gevent import monkey
- monkey.patch_thread()
- monkey.patch_time()
-
- def run_server():
- self.wsgi_server.serve_forever()
-
- run_with_reloader(run_server, extra_files=extra_files)
- else:
- self.wsgi_server.serve_forever()
-
- def stop(self):
- """Stop a running SocketIO web server.
-
- This method must be called from a HTTP or SocketIO handler function.
- """
- if self.server.eio.async_mode == 'threading':
- func = flask.request.environ.get('werkzeug.server.shutdown')
- if func:
- func()
- else:
- raise RuntimeError('Cannot stop unknown web server')
- elif self.server.eio.async_mode == 'eventlet':
- raise SystemExit
- elif self.server.eio.async_mode == 'gevent':
- self.wsgi_server.stop()
-
- def start_background_task(self, target, *args, **kwargs):
- """Start a background task using the appropriate async model.
-
- This is a utility function that applications can use to start a
- background task using the method that is compatible with the
- selected async mode.
-
- :param target: the target function to execute.
- :param args: arguments to pass to the function.
- :param kwargs: keyword arguments to pass to the function.
-
- This function returns an object compatible with the `Thread` class in
- the Python standard library. The `start()` method on this object is
- already called by this function.
- """
- return self.server.start_background_task(target, *args, **kwargs)
-
- def sleep(self, seconds=0):
- """Sleep for the requested amount of time using the appropriate async
- model.
-
- This is a utility function that applications can use to put a task to
- sleep without having to worry about using the correct call for the
- selected async mode.
- """
- return self.server.sleep(seconds)
-
- def test_client(self, app, namespace=None, query_string=None,
- headers=None, flask_test_client=None):
- """The Socket.IO test client is useful for testing a Flask-SocketIO
- server. It works in a similar way to the Flask Test Client, but
- adapted to the Socket.IO server.
-
- :param app: The Flask application instance.
- :param namespace: The namespace for the client. If not provided, the
- client connects to the server on the global
- namespace.
- :param query_string: A string with custom query string arguments.
- :param headers: A dictionary with custom HTTP headers.
- :param flask_test_client: The instance of the Flask test client
- currently in use. Passing the Flask test
- client is optional, but is necessary if you
- want the Flask user session and any other
- cookies set in HTTP routes accessible from
- Socket.IO events.
- """
- return SocketIOTestClient(app, self, namespace=namespace,
- query_string=query_string, headers=headers,
- flask_test_client=flask_test_client)
-
- def _handle_event(self, handler, message, namespace, sid, *args):
- if sid not in self.server.environ:
- # we don't have record of this client, ignore this event
- return '', 400
- app = self.server.environ[sid]['flask.app']
- with app.request_context(self.server.environ[sid]):
- if self.manage_session:
- # manage a separate session for this client's Socket.IO events
- # created as a copy of the regular user session
- if 'saved_session' not in self.server.environ[sid]:
- self.server.environ[sid]['saved_session'] = \
- _ManagedSession(flask.session)
- session_obj = self.server.environ[sid]['saved_session']
- else:
- # let Flask handle the user session
- # for cookie based sessions, this effectively freezes the
- # session to its state at connection time
- # for server-side sessions, this allows HTTP and Socket.IO to
- # share the session, with both having read/write access to it
- session_obj = flask.session._get_current_object()
- _request_ctx_stack.top.session = session_obj
- flask.request.sid = sid
- flask.request.namespace = namespace
- flask.request.event = {'message': message, 'args': args}
- try:
- if message == 'connect':
- ret = handler()
- else:
- ret = handler(*args)
- except:
- err_handler = self.exception_handlers.get(
- namespace, self.default_exception_handler)
- if err_handler is None:
- raise
- type, value, traceback = sys.exc_info()
- return err_handler(value)
- if not self.manage_session:
- # when Flask is managing the user session, it needs to save it
- if not hasattr(session_obj, 'modified') or session_obj.modified:
- resp = app.response_class()
- app.session_interface.save_session(app, session_obj, resp)
- return ret
-
-
- def emit(event, *args, **kwargs):
- """Emit a SocketIO event.
-
- This function emits a SocketIO event to one or more connected clients. A
- JSON blob can be attached to the event as payload. This is a function that
- can only be called from a SocketIO event handler, as in obtains some
- information from the current client context. Example::
-
- @socketio.on('my event')
- def handle_my_custom_event(json):
- emit('my response', {'data': 42})
-
- :param event: The name of the user event to emit.
- :param args: A dictionary with the JSON data to send as payload.
- :param namespace: The namespace under which the message is to be sent.
- Defaults to the namespace used by the originating event.
- A ``'/'`` can be used to explicitly specify the global
- namespace.
- :param callback: Callback function to invoke with the client's
- acknowledgement.
- :param broadcast: ``True`` to send the message to all clients, or ``False``
- to only reply to the sender of the originating event.
- :param room: Send the message to all the users in the given room. If this
- argument is set, then broadcast is implied to be ``True``.
- :param include_self: ``True`` to include the sender when broadcasting or
- addressing a room, or ``False`` to send to everyone
- but the sender.
- :param ignore_queue: Only used when a message queue is configured. If
- set to ``True``, the event is emitted to the
- clients directly, without going through the queue.
- This is more efficient, but only works when a
- single server process is used, or when there is a
- single addresee. It is recommended to always leave
- this parameter with its default value of ``False``.
- """
- if 'namespace' in kwargs:
- namespace = kwargs['namespace']
- else:
- namespace = flask.request.namespace
- callback = kwargs.get('callback')
- broadcast = kwargs.get('broadcast')
- room = kwargs.get('room')
- if room is None and not broadcast:
- room = flask.request.sid
- include_self = kwargs.get('include_self', True)
- ignore_queue = kwargs.get('ignore_queue', False)
-
- socketio = flask.current_app.extensions['socketio']
- return socketio.emit(event, *args, namespace=namespace, room=room,
- include_self=include_self, callback=callback,
- ignore_queue=ignore_queue)
-
-
- def send(message, **kwargs):
- """Send a SocketIO message.
-
- This function sends a simple SocketIO message to one or more connected
- clients. The message can be a string or a JSON blob. This is a simpler
- version of ``emit()``, which should be preferred. This is a function that
- can only be called from a SocketIO event handler.
-
- :param message: The message to send, either a string or a JSON blob.
- :param json: ``True`` if ``message`` is a JSON blob, ``False``
- otherwise.
- :param namespace: The namespace under which the message is to be sent.
- Defaults to the namespace used by the originating event.
- An empty string can be used to use the global namespace.
- :param callback: Callback function to invoke with the client's
- acknowledgement.
- :param broadcast: ``True`` to send the message to all connected clients, or
- ``False`` to only reply to the sender of the originating
- event.
- :param room: Send the message to all the users in the given room.
- :param include_self: ``True`` to include the sender when broadcasting or
- addressing a room, or ``False`` to send to everyone
- but the sender.
- :param ignore_queue: Only used when a message queue is configured. If
- set to ``True``, the event is emitted to the
- clients directly, without going through the queue.
- This is more efficient, but only works when a
- single server process is used, or when there is a
- single addresee. It is recommended to always leave
- this parameter with its default value of ``False``.
- """
- json = kwargs.get('json', False)
- if 'namespace' in kwargs:
- namespace = kwargs['namespace']
- else:
- namespace = flask.request.namespace
- callback = kwargs.get('callback')
- broadcast = kwargs.get('broadcast')
- room = kwargs.get('room')
- if room is None and not broadcast:
- room = flask.request.sid
- include_self = kwargs.get('include_self', True)
- ignore_queue = kwargs.get('ignore_queue', False)
-
- socketio = flask.current_app.extensions['socketio']
- return socketio.send(message, json=json, namespace=namespace, room=room,
- include_self=include_self, callback=callback,
- ignore_queue=ignore_queue)
-
-
- def join_room(room, sid=None, namespace=None):
- """Join a room.
-
- This function puts the user in a room, under the current namespace. The
- user and the namespace are obtained from the event context. This is a
- function that can only be called from a SocketIO event handler. Example::
-
- @socketio.on('join')
- def on_join(data):
- username = session['username']
- room = data['room']
- join_room(room)
- send(username + ' has entered the room.', room=room)
-
- :param room: The name of the room to join.
- :param sid: The session id of the client. If not provided, the client is
- obtained from the request context.
- :param namespace: The namespace for the room. If not provided, the
- namespace is obtained from the request context.
- """
- socketio = flask.current_app.extensions['socketio']
- sid = sid or flask.request.sid
- namespace = namespace or flask.request.namespace
- socketio.server.enter_room(sid, room, namespace=namespace)
-
-
- def leave_room(room, sid=None, namespace=None):
- """Leave a room.
-
- This function removes the user from a room, under the current namespace.
- The user and the namespace are obtained from the event context. Example::
-
- @socketio.on('leave')
- def on_leave(data):
- username = session['username']
- room = data['room']
- leave_room(room)
- send(username + ' has left the room.', room=room)
-
- :param room: The name of the room to leave.
- :param sid: The session id of the client. If not provided, the client is
- obtained from the request context.
- :param namespace: The namespace for the room. If not provided, the
- namespace is obtained from the request context.
- """
- socketio = flask.current_app.extensions['socketio']
- sid = sid or flask.request.sid
- namespace = namespace or flask.request.namespace
- socketio.server.leave_room(sid, room, namespace=namespace)
-
-
- def close_room(room, namespace=None):
- """Close a room.
-
- This function removes any users that are in the given room and then deletes
- the room from the server.
-
- :param room: The name of the room to close.
- :param namespace: The namespace for the room. If not provided, the
- namespace is obtained from the request context.
- """
- socketio = flask.current_app.extensions['socketio']
- namespace = namespace or flask.request.namespace
- socketio.server.close_room(room, namespace=namespace)
-
-
- def rooms(sid=None, namespace=None):
- """Return a list of the rooms the client is in.
-
- This function returns all the rooms the client has entered, including its
- own room, assigned by the Socket.IO server.
-
- :param sid: The session id of the client. If not provided, the client is
- obtained from the request context.
- :param namespace: The namespace for the room. If not provided, the
- namespace is obtained from the request context.
- """
- socketio = flask.current_app.extensions['socketio']
- sid = sid or flask.request.sid
- namespace = namespace or flask.request.namespace
- return socketio.server.rooms(sid, namespace=namespace)
-
-
- def disconnect(sid=None, namespace=None, silent=False):
- """Disconnect the client.
-
- This function terminates the connection with the client. As a result of
- this call the client will receive a disconnect event. Example::
-
- @socketio.on('message')
- def receive_message(msg):
- if is_banned(session['username']):
- disconnect()
- else:
- # ...
-
- :param sid: The session id of the client. If not provided, the client is
- obtained from the request context.
- :param namespace: The namespace for the room. If not provided, the
- namespace is obtained from the request context.
- :param silent: this option is deprecated.
- """
- socketio = flask.current_app.extensions['socketio']
- sid = sid or flask.request.sid
- namespace = namespace or flask.request.namespace
- return socketio.server.disconnect(sid, namespace=namespace)
|