From ea2360a9b13ce47d428afa7f2ce2509fc489b8ee Mon Sep 17 00:00:00 2001 From: Stephen Diehl Date: Tue, 6 Mar 2012 19:57:58 -0500 Subject: [PATCH] More granularity in session failure modes. --- gevent_sockjs/devserver.py | 14 ++++++++++++++ gevent_sockjs/router.py | 2 +- gevent_sockjs/session.py | 31 ++++++++++++++++++++++++++++--- gevent_sockjs/transports.py | 8 ++++++++ 4 files changed, 51 insertions(+), 4 deletions(-) diff --git a/gevent_sockjs/devserver.py b/gevent_sockjs/devserver.py index a9dba9f..ec048d4 100644 --- a/gevent_sockjs/devserver.py +++ b/gevent_sockjs/devserver.py @@ -1,4 +1,18 @@ +""" +This module is most like what a user would define in their +application, namely the + + - Routes + - Connection Handlers + +The one's sketched here are the Echo, Disabled Websockets, and +the Close connection handlers which are used by the protocol test +suite. +""" + import gevent.monkey + +# Moneky patching stdlib is not a neccesity for all use cases gevent.monkey.patch_all() from server import SockJSServer diff --git a/gevent_sockjs/router.py b/gevent_sockjs/router.py index ce68a8a..a33b1ff 100644 --- a/gevent_sockjs/router.py +++ b/gevent_sockjs/router.py @@ -103,7 +103,7 @@ def broadcast(self, channel, message): def close(self): if self.session: - self.session.kill() + self.session.interrupt() else: raise Exception("Tried to close closed session") diff --git a/gevent_sockjs/session.py b/gevent_sockjs/session.py index d739c00..cbad391 100644 --- a/gevent_sockjs/session.py +++ b/gevent_sockjs/session.py @@ -23,6 +23,15 @@ def __init__(self, server, session_id=None): self.forever = False self.session_id = self.generate_uid() + # Whether this was closed explictly by client vs + # internally by garbage collection. + self.interrupted = False + + # When a polling request is closed by a network error - not by + # server, the session should be automatically closed. When there + # is a network error - we're in an undefined state. Some messages + # may have been lost, there is not much we can do about it. + self.network_error = False # Async event, use rawlink to string callbacks self.timeout = Event() @@ -57,6 +66,10 @@ def persist(self, extension=None, forever=False): def post_delete(self): pass + def kill(self): + self.killed = True + self.expire() + def expire(self): """ Manually expire a session. @@ -81,15 +94,18 @@ def add_message(self, msg): def get_messages(self, **kwargs): raise NotImplemented() - def kill(self): - raise NotImplemented() - def is_locked(self): return self.locked.is_set() + def is_network_error(self): + return self.network_error + def is_expired(self): return self.expired + def is_interrupted(self): + return self.interrupted + def lock(self): self.locked.set() @@ -137,6 +153,15 @@ def get_messages(self, **kwargs): finally: return accum + def interrupt(self): + """ + A kill event trigged through a client accessible endpoint + + Internal expires will not have is_interupted() == True + """ + self.interrupted = True + self.kill() + def kill(self): self.connected = False diff --git a/gevent_sockjs/transports.py b/gevent_sockjs/transports.py index b6ff628..8f63a1f 100644 --- a/gevent_sockjs/transports.py +++ b/gevent_sockjs/transports.py @@ -179,12 +179,20 @@ def __call__(self, handler, request_method, raw_request_data): handler.enable_cors() handler.write_js(protocol.OPEN) return [] + + elif self.session.is_network_error(): + interrupt_error = protocol.close_frame(1002, "Connection interrupted") + handler.write_text(interrupt_error) + return [] + elif self.session.is_expired(): close_error = protocol.close_frame(3000, "Go away!") handler.write_text(close_error) return [] + elif self.session.is_locked(): lock_error = protocol.close_frame(2010, "Another connection still open") + self.session.network_error = True handler.write_text(lock_error) return [] else: