From 26172f41d889cd82860cfd76e39fc3b9e6d8b8db Mon Sep 17 00:00:00 2001 From: Miguel Grinberg Date: Sun, 18 Oct 2015 12:18:35 -0700 Subject: [PATCH] Do not fork the session unless it is modified --- docs/index.rst | 21 ++++++++++++--------- flask_socketio/__init__.py | 9 +++++---- test_socketio.py | 7 +++++-- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 50338ce3..0037589f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -407,7 +407,7 @@ regular HTTP request. The following list describes what works and what doesn't: - An application context is pushed before invoking an event handler making ``current_app`` and ``g`` available to the handler. - A request context is also pushed before invoking a handler, also making - ``request`` and ``session`` available. Note that WebSocket events do not + ``request`` and ``session`` available. But note that WebSocket events do not have individual requests associated with them, so the request context that started the connection is pushed for all the events that are dispatched during the life of the connection. @@ -420,14 +420,17 @@ regular HTTP request. The following list describes what works and what doesn't: - The ``session`` context global behaves in a different way than in regular requests. A copy of the user session at the time the SocketIO connection is established is made available to handlers invoked in the context of that - connection. Any changes made to the session inside a SocketIO handler are - preserved, but only in the SocketIO context, these changes will not be seen - by regular HTTP handlers. The technical reason for this limitation is that to - save the user session a cookie needs to be sent to the client, and that - requires HTTP request and response, which do not exist in a socket - connection. When using server-side session storage SocketIO handlers can - update user sessions even for HTTP routes (see the - `Flask-KVsession `_ extension). + connection. If a SocketIO handler modifies the session, the modified session + will be preserved for future SocketIO handlers, but regular HTTP route + handlers will not see these changes. Effectively, when a SocketIO handler + modifies the session, a "fork" of the session is created exclusively for + these handlers. The technical reason for this limitation is that to save the + user session a cookie needs to be sent to the client, and that requires HTTP + request and response, which do not exist in a SocketIO connection. When + using server-side sessions such as those provided by the Flask-Session or + Flask-KVSession extensions, changes made to the session in HTTP route + handlers can be seen by SocketIO handlers, as long as the session is not + modified in the SocketIO handlers. - The ``before_request`` and ``after_request`` hooks are not invoked for SocketIO event handlers. - SocketIO handlers can take custom decorators, but most Flask decorators will diff --git a/flask_socketio/__init__.py b/flask_socketio/__init__.py index adedcce0..f98265b5 100644 --- a/flask_socketio/__init__.py +++ b/flask_socketio/__init__.py @@ -148,10 +148,11 @@ def _handler(sid, *args): raise type, value, traceback = sys.exc_info() return err_handler(value) - self.server.environ[sid]['saved_session'] = {} - self._copy_session( - flask.session, - self.server.environ[sid]['saved_session']) + if flask.session.modified: + self.server.environ[sid]['saved_session'] = {} + self._copy_session( + flask.session, + self.server.environ[sid]['saved_session']) return ret self.server.on(message, _handler, namespace=namespace) return _handler diff --git a/test_socketio.py b/test_socketio.py index ca68efe5..15e90512 100755 --- a/test_socketio.py +++ b/test_socketio.py @@ -16,7 +16,6 @@ @socketio.on('connect') def on_connect(): send('connected') - session['a'] = 'b' @socketio.on('disconnect') @@ -39,6 +38,8 @@ def on_disconnect_test(): @socketio.on('message') def on_message(message): send(message) + if message == 'test session': + session['a'] = 'b' if message not in "test noack": return message @@ -264,7 +265,7 @@ def test_request_event_data(self): global request_event_data request_event_data = None client.emit('other custom event', 'foo') - expected_data = {'message': 'other custom event', 'args' : ('foo',)} + expected_data = {'message': 'other custom event', 'args': ('foo',)} self.assertTrue(request_event_data == expected_data) def test_emit_namespace(self): @@ -310,6 +311,8 @@ def test_session(self): client = socketio.test_client(app) client.get_received() client.send('echo this message back') + self.assertNotIn('saved_session', socketio.server.environ[client.sid]) + client.send('test session') session = socketio.server.environ[client.sid]['saved_session'] self.assertTrue(session['a'] == 'b')