Skip to content

Commit

Permalink
Allow more than one application per socketio instance (fixes #146)
Browse files Browse the repository at this point in the history
  • Loading branch information
miguelgrinberg committed Oct 15, 2015
1 parent 1f61ea0 commit 5f0dfbe
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 20 deletions.
2 changes: 1 addition & 1 deletion example/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,4 @@ def test_disconnect():


if __name__ == '__main__':
socketio.run(debug=True)
socketio.run(app, debug=True)
36 changes: 21 additions & 15 deletions flask_socketio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,22 @@
from .test_client import SocketIOTestClient


class _SocketIOMiddleware(socketio.Middleware):
"""This WSGI middleware simply pushes a Flask application context before
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)

def __call__(self, environ, start_response):
with self.flask_app.app_context():
return super(_SocketIOMiddleware, self).__call__(environ,
start_response)


class SocketIO(object):
"""Create a Flask-SocketIO server.
Expand Down Expand Up @@ -68,7 +84,6 @@ class SocketIO(object):
"""

def __init__(self, app=None, **kwargs):
self.app = None
self.server = None
self.server_options = None
self.exception_handlers = {}
Expand All @@ -77,20 +92,16 @@ def __init__(self, app=None, **kwargs):
self.init_app(app, **kwargs)

def init_app(self, app, **kwargs):
if self.app is not None and self.app != app:
raise RuntimeError('Cannot associate a SocketIO instance with '
'more than one application')
if not hasattr(app, 'extensions'):
app.extensions = {} # pragma: no cover
app.extensions['socketio'] = self
self.app = app
self.server_options = kwargs

resource = kwargs.get('resource', 'socket.io')
if resource.startswith('/'):
resource = resource[1:]
self.server = socketio.Server(**self.server_options)
app.wsgi_app = socketio.Middleware(self.server, app.wsgi_app,
app.wsgi_app = _SocketIOMiddleware(self.server, app,
socketio_path=resource)

def on(self, message, namespace=None):
Expand All @@ -116,7 +127,8 @@ def handle_my_custom_event(json):

def decorator(handler):
def _handler(sid, *args):
with self.app.request_context(self.server.environ[sid]):
with flask.current_app.request_context(
self.server.environ[sid]):
if 'saved_session' in self.server.environ[sid]:
self._copy_session(
self.server.environ[sid]['saved_session'],
Expand Down Expand Up @@ -268,11 +280,10 @@ def close_room(self, room, namespace=None):
"""
self.server.close_room(room, namespace)

def run(self, app=None, host=None, port=None, **kwargs):
def run(self, app, host=None, port=None, **kwargs):
"""Run the SocketIO web server.
:param app: The Flask application instance. This argument is kept for
legacy reasons, it can be omitted.
: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
Expand All @@ -294,11 +305,6 @@ def run(self, app=None, host=None, port=None, **kwargs):
available when using an external web server, since this
method will not be called.
"""
if app is None:
app = self.app
else:
self.app = app

if host is None:
host = '127.0.0.1'
if port is None:
Expand Down
14 changes: 10 additions & 4 deletions flask_socketio/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def _mock_send_packet(sid, pkt):
self.ack = {'args': pkt.data,
'namespace': pkt.namespace or '/'}

self.app = app
self.sid = uuid.uuid4().hex
self.callback_counter = 0
self.socketio = socketio
Expand All @@ -38,15 +39,19 @@ def _mock_send_packet(sid, pkt):
def connect(self, namespace=None):
"""Connect the client."""
environ = EnvironBuilder('/socket.io').get_environ()
self.socketio.server._handle_eio_connect(self.sid, environ)
with self.app.app_context():
self.socketio.server._handle_eio_connect(self.sid, environ)
if namespace is not None and namespace != '/':
pkt = packet.Packet(packet.CONNECT, namespace=namespace)
self.socketio.server._handle_eio_message(self.sid, pkt.encode())
with self.app.app_context():
self.socketio.server._handle_eio_message(self.sid,
pkt.encode())

def disconnect(self, namespace=None):
"""Disconnect the client."""
pkt = packet.Packet(packet.DISCONNECT, namespace=namespace)
self.socketio.server._handle_eio_message(self.sid, pkt.encode())
with self.app.app_context():
self.socketio.server._handle_eio_message(self.sid, pkt.encode())

def emit(self, event, *args, **kwargs):
"""Emit an event to the server."""
Expand All @@ -59,7 +64,8 @@ def emit(self, event, *args, **kwargs):
pkt = packet.Packet(packet.EVENT, data=[event] + list(args),
namespace=namespace, id=id, binary=False)
self.ack = None
self.socketio.server._handle_eio_message(self.sid, pkt.encode())
with self.app.app_context():
self.socketio.server._handle_eio_message(self.sid, pkt.encode())
if self.ack is not None:
return self.ack['args'][0] if len(self.ack['args']) == 1 \
else self.ack['args']
Expand Down

0 comments on commit 5f0dfbe

Please sign in to comment.