From 9cc4bd4d886f301823948b51666aa535056cc9f5 2013-05-15 21:39:31 From: Brian E. Granger Date: 2013-05-15 21:39:31 Subject: [PATCH] Moving base ZMQ handlers to base/zmqhandlers.py. --- diff --git a/IPython/frontend/html/notebook/base/zmqhandlers.py b/IPython/frontend/html/notebook/base/zmqhandlers.py new file mode 100644 index 0000000..24bb26a --- /dev/null +++ b/IPython/frontend/html/notebook/base/zmqhandlers.py @@ -0,0 +1,114 @@ +"""Tornado handlers for WebSocket <-> ZMQ sockets. + +Authors: + +* Brian Granger +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2008-2011 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +import Cookie +import logging +from tornado import web +from tornado import websocket + +from zmq.utils import jsonapi + +from IPython.kernel.zmq.session import Session +from IPython.utils.jsonutil import date_default +from IPython.utils.py3compat import PY3 + +from ..base.handlers import IPythonHandler + +#----------------------------------------------------------------------------- +# ZMQ handlers +#----------------------------------------------------------------------------- + +class ZMQStreamHandler(websocket.WebSocketHandler): + + def clear_cookie(self, *args, **kwargs): + """meaningless for websockets""" + pass + + def _reserialize_reply(self, msg_list): + """Reserialize a reply message using JSON. + + This takes the msg list from the ZMQ socket, unserializes it using + self.session and then serializes the result using JSON. This method + should be used by self._on_zmq_reply to build messages that can + be sent back to the browser. + """ + idents, msg_list = self.session.feed_identities(msg_list) + msg = self.session.unserialize(msg_list) + try: + msg['header'].pop('date') + except KeyError: + pass + try: + msg['parent_header'].pop('date') + except KeyError: + pass + msg.pop('buffers') + return jsonapi.dumps(msg, default=date_default) + + def _on_zmq_reply(self, msg_list): + # Sometimes this gets triggered when the on_close method is scheduled in the + # eventloop but hasn't been called. + if self.stream.closed(): return + try: + msg = self._reserialize_reply(msg_list) + except Exception: + self.log.critical("Malformed message: %r" % msg_list, exc_info=True) + else: + self.write_message(msg) + + def allow_draft76(self): + """Allow draft 76, until browsers such as Safari update to RFC 6455. + + This has been disabled by default in tornado in release 2.2.0, and + support will be removed in later versions. + """ + return True + + +class AuthenticatedZMQStreamHandler(ZMQStreamHandler, IPythonHandler): + + def open(self, kernel_id): + self.kernel_id = kernel_id.decode('ascii') + self.session = Session(config=self.config) + self.save_on_message = self.on_message + self.on_message = self.on_first_message + + def _inject_cookie_message(self, msg): + """Inject the first message, which is the document cookie, + for authentication.""" + if not PY3 and isinstance(msg, unicode): + # Cookie constructor doesn't accept unicode strings + # under Python 2.x for some reason + msg = msg.encode('utf8', 'replace') + try: + identity, msg = msg.split(':', 1) + self.session.session = identity.decode('ascii') + except Exception: + logging.error("First ws message didn't have the form 'identity:[cookie]' - %r", msg) + + try: + self.request._cookies = Cookie.SimpleCookie(msg) + except: + self.log.warn("couldn't parse cookie string: %s",msg, exc_info=True) + + def on_first_message(self, msg): + self._inject_cookie_message(msg) + if self.get_current_user() is None: + self.log.warn("Couldn't authenticate WebSocket connection") + raise web.HTTPError(403) + self.on_message = self.save_on_message \ No newline at end of file diff --git a/IPython/frontend/html/notebook/kernels/apihandlers.py b/IPython/frontend/html/notebook/kernels/apihandlers.py index 67e2d8f..a903255 100644 --- a/IPython/frontend/html/notebook/kernels/apihandlers.py +++ b/IPython/frontend/html/notebook/kernels/apihandlers.py @@ -16,18 +16,15 @@ Authors: # Imports #----------------------------------------------------------------------------- -import Cookie import logging from tornado import web -from tornado import websocket from zmq.utils import jsonapi -from IPython.kernel.zmq.session import Session from IPython.utils.jsonutil import date_default -from IPython.utils.py3compat import PY3 from ..base.handlers import IPythonHandler +from ..base.zmqhandlers import AuthenticatedZMQStreamHandler #----------------------------------------------------------------------------- # Kernel handlers @@ -80,87 +77,6 @@ class KernelActionHandler(IPythonHandler): self.finish() -class ZMQStreamHandler(websocket.WebSocketHandler): - - def clear_cookie(self, *args, **kwargs): - """meaningless for websockets""" - pass - - def _reserialize_reply(self, msg_list): - """Reserialize a reply message using JSON. - - This takes the msg list from the ZMQ socket, unserializes it using - self.session and then serializes the result using JSON. This method - should be used by self._on_zmq_reply to build messages that can - be sent back to the browser. - """ - idents, msg_list = self.session.feed_identities(msg_list) - msg = self.session.unserialize(msg_list) - try: - msg['header'].pop('date') - except KeyError: - pass - try: - msg['parent_header'].pop('date') - except KeyError: - pass - msg.pop('buffers') - return jsonapi.dumps(msg, default=date_default) - - def _on_zmq_reply(self, msg_list): - # Sometimes this gets triggered when the on_close method is scheduled in the - # eventloop but hasn't been called. - if self.stream.closed(): return - try: - msg = self._reserialize_reply(msg_list) - except Exception: - self.log.critical("Malformed message: %r" % msg_list, exc_info=True) - else: - self.write_message(msg) - - def allow_draft76(self): - """Allow draft 76, until browsers such as Safari update to RFC 6455. - - This has been disabled by default in tornado in release 2.2.0, and - support will be removed in later versions. - """ - return True - - -class AuthenticatedZMQStreamHandler(ZMQStreamHandler, IPythonHandler): - - def open(self, kernel_id): - self.kernel_id = kernel_id.decode('ascii') - self.session = Session(config=self.config) - self.save_on_message = self.on_message - self.on_message = self.on_first_message - - def _inject_cookie_message(self, msg): - """Inject the first message, which is the document cookie, - for authentication.""" - if not PY3 and isinstance(msg, unicode): - # Cookie constructor doesn't accept unicode strings - # under Python 2.x for some reason - msg = msg.encode('utf8', 'replace') - try: - identity, msg = msg.split(':', 1) - self.session.session = identity.decode('ascii') - except Exception: - logging.error("First ws message didn't have the form 'identity:[cookie]' - %r", msg) - - try: - self.request._cookies = Cookie.SimpleCookie(msg) - except: - self.log.warn("couldn't parse cookie string: %s",msg, exc_info=True) - - def on_first_message(self, msg): - self._inject_cookie_message(msg) - if self.get_current_user() is None: - self.log.warn("Couldn't authenticate WebSocket connection") - raise web.HTTPError(403) - self.on_message = self.save_on_message - - class ZMQChannelHandler(AuthenticatedZMQStreamHandler): @property