From 4fe10b8142e3b4a8b87287c2a57e0c4d9b6874ee Mon Sep 17 00:00:00 2001 From: Min RK Date: Fri, 20 May 2016 10:44:12 +0200 Subject: [PATCH] Prevent session_id collisions Keep a registry of open sessions, and refuse to open duplicates. --- notebook/services/kernels/handlers.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/notebook/services/kernels/handlers.py b/notebook/services/kernels/handlers.py index 3b50a10c2..c64a9853c 100644 --- a/notebook/services/kernels/handlers.py +++ b/notebook/services/kernels/handlers.py @@ -16,7 +16,7 @@ from jupyter_client.jsonutil import date_default from ipython_genutils.py3compat import cast_unicode from notebook.utils import url_path_join, url_escape -from ...base.handlers import IPythonHandler, APIHandler, json_errors +from ...base.handlers import APIHandler, json_errors from ...base.zmqhandlers import AuthenticatedZMQStreamHandler, deserialize_binary_message from jupyter_client import protocol_version as client_protocol_version @@ -96,6 +96,12 @@ class KernelActionHandler(APIHandler): class ZMQChannelsHandler(AuthenticatedZMQStreamHandler): + + # class-level registry of open sessions + # allows checking for conflict on session-id, + # which is used as a zmq identity and must be unique. + session_key = '' + _open_sessions = {} @property def kernel_info_timeout(self): @@ -209,6 +215,8 @@ class ZMQChannelsHandler(AuthenticatedZMQStreamHandler): def pre_get(self): # authenticate first super(ZMQChannelsHandler, self).pre_get() + # check session collision: + self._register_session() # then request kernel info, waiting up to a certain time before giving up. # We don't want to wait forever, because browsers don't take it well when # servers never respond to websocket connection requests. @@ -232,6 +240,13 @@ class ZMQChannelsHandler(AuthenticatedZMQStreamHandler): self.kernel_id = cast_unicode(kernel_id, 'ascii') yield super(ZMQChannelsHandler, self).get(kernel_id=kernel_id) + def _register_session(self): + """Ensure we aren't creating a duplicate session""" + self.session_key = '%s:%s' % (self.kernel_id, self.session.session) + if self.session_key in self._open_sessions: + raise web.HTTPError(400, "Session %s already open." % self.session_key) + self._open_sessions[self.session_key] = self + def open(self, kernel_id): super(ZMQChannelsHandler, self).open() try: @@ -350,6 +365,10 @@ class ZMQChannelsHandler(AuthenticatedZMQStreamHandler): def on_close(self): + self.log.debug("Websocket closed %s", self.session_key) + # unregister myself as an open session (only if it's really me) + if self._open_sessions.get(self.session_key) is self: + self._open_sessions.pop(self.session_key) km = self.kernel_manager if self.kernel_id in km: km.remove_restart_callback(