mirror of
https://github.com/jupyter/notebook.git
synced 2025-01-24 12:05:22 +08:00
debugging websocket connections
- add debug statement at the very beginning of a web socket request - add debug statement in open, indicating that the connection has been accepted - add timeout, so failed or slow kernel_info doesn't cause the request to never get a response - don't send kernel_info_request before authenticating the request The last one required some icky coroutine shenanigans, because of our subclass structure, but it should work fine.
This commit is contained in:
parent
b26706ba69
commit
e5b135a905
@ -13,9 +13,7 @@ except ImportError:
|
|||||||
from urlparse import urlparse # Py 2
|
from urlparse import urlparse # Py 2
|
||||||
|
|
||||||
import tornado
|
import tornado
|
||||||
from tornado import ioloop
|
from tornado import gen, ioloop, web, websocket
|
||||||
from tornado import web
|
|
||||||
from tornado import websocket
|
|
||||||
|
|
||||||
from IPython.kernel.zmq.session import Session
|
from IPython.kernel.zmq.session import Session
|
||||||
from IPython.utils.jsonutil import date_default, extract_dates
|
from IPython.utils.jsonutil import date_default, extract_dates
|
||||||
@ -197,7 +195,12 @@ class AuthenticatedZMQStreamHandler(ZMQStreamHandler, IPythonHandler):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
def pre_get(self):
|
||||||
|
"""Run before finishing the GET request
|
||||||
|
|
||||||
|
Extend this method to add logic that should fire before
|
||||||
|
the websocket finishes completing.
|
||||||
|
"""
|
||||||
# Check to see that origin matches host directly, including ports
|
# Check to see that origin matches host directly, including ports
|
||||||
# Tornado 4 already does CORS checking
|
# Tornado 4 already does CORS checking
|
||||||
if tornado.version_info[0] < 4:
|
if tornado.version_info[0] < 4:
|
||||||
@ -213,15 +216,22 @@ class AuthenticatedZMQStreamHandler(ZMQStreamHandler, IPythonHandler):
|
|||||||
self.session.session = cast_unicode(self.get_argument('session_id'))
|
self.session.session = cast_unicode(self.get_argument('session_id'))
|
||||||
else:
|
else:
|
||||||
self.log.warn("No session ID specified")
|
self.log.warn("No session ID specified")
|
||||||
|
|
||||||
|
@gen.coroutine
|
||||||
|
def get(self, *args, **kwargs):
|
||||||
|
# pre_get can be a coroutine in subclasses
|
||||||
|
yield gen.maybe_future(self.pre_get())
|
||||||
# FIXME: only do super get on tornado ≥ 4
|
# FIXME: only do super get on tornado ≥ 4
|
||||||
# tornado 3 has no get, will raise 405
|
# tornado 3 has no get, will raise 405
|
||||||
if tornado.version_info >= (4,):
|
if tornado.version_info >= (4,):
|
||||||
return super(AuthenticatedZMQStreamHandler, self).get(*args, **kwargs)
|
super(AuthenticatedZMQStreamHandler, self).get(*args, **kwargs)
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
|
self.log.debug("Initializing websocket connection %s", self.request.path)
|
||||||
self.session = Session(config=self.config)
|
self.session = Session(config=self.config)
|
||||||
|
|
||||||
def open(self, *args, **kwargs):
|
def open(self, *args, **kwargs):
|
||||||
|
self.log.debug("Opening websocket %s", self.request.path)
|
||||||
if tornado.version_info < (4,):
|
if tornado.version_info < (4,):
|
||||||
try:
|
try:
|
||||||
self.get(*self.open_args, **self.open_kwargs)
|
self.get(*self.open_args, **self.open_kwargs)
|
||||||
|
@ -7,6 +7,7 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
from tornado import gen, web
|
from tornado import gen, web
|
||||||
from tornado.concurrent import Future
|
from tornado.concurrent import Future
|
||||||
|
from tornado.ioloop import IOLoop
|
||||||
|
|
||||||
from IPython.utils.jsonutil import date_default
|
from IPython.utils.jsonutil import date_default
|
||||||
from IPython.utils.py3compat import cast_unicode
|
from IPython.utils.py3compat import cast_unicode
|
||||||
@ -85,6 +86,10 @@ class KernelActionHandler(IPythonHandler):
|
|||||||
|
|
||||||
class ZMQChannelHandler(AuthenticatedZMQStreamHandler):
|
class ZMQChannelHandler(AuthenticatedZMQStreamHandler):
|
||||||
|
|
||||||
|
@property
|
||||||
|
def kernel_info_timeout(self):
|
||||||
|
return self.settings.get('kernel_info_timeout', 10)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(%s)" % (self.__class__.__name__, getattr(self, 'kernel_id', 'uninitialized'))
|
return "%s(%s)" % (self.__class__.__name__, getattr(self, 'kernel_id', 'uninitialized'))
|
||||||
|
|
||||||
@ -150,6 +155,7 @@ class ZMQChannelHandler(AuthenticatedZMQStreamHandler):
|
|||||||
if protocol_version != kernel_protocol_version:
|
if protocol_version != kernel_protocol_version:
|
||||||
self.session.adapt_version = int(protocol_version.split('.')[0])
|
self.session.adapt_version = int(protocol_version.split('.')[0])
|
||||||
self.log.info("Kernel %s speaks protocol %s", self.kernel_id, protocol_version)
|
self.log.info("Kernel %s speaks protocol %s", self.kernel_id, protocol_version)
|
||||||
|
if not self._kernel_info_future.done():
|
||||||
self._kernel_info_future.set_result(info)
|
self._kernel_info_future.set_result(info)
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
@ -159,11 +165,30 @@ class ZMQChannelHandler(AuthenticatedZMQStreamHandler):
|
|||||||
self.kernel_info_channel = None
|
self.kernel_info_channel = None
|
||||||
self._kernel_info_future = Future()
|
self._kernel_info_future = Future()
|
||||||
|
|
||||||
|
@gen.coroutine
|
||||||
|
def pre_get(self):
|
||||||
|
# authenticate first
|
||||||
|
super(ZMQChannelHandler, self).pre_get()
|
||||||
|
# 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.
|
||||||
|
future = self.request_kernel_info()
|
||||||
|
|
||||||
|
def give_up():
|
||||||
|
"""Don't wait forever for the kernel to reply"""
|
||||||
|
if future.done():
|
||||||
|
return
|
||||||
|
self.log.warn("Timeout waiting for kernel_info reply from %s", self.kernel_id)
|
||||||
|
future.set_result(None)
|
||||||
|
loop = IOLoop.current()
|
||||||
|
loop.add_timeout(loop.time() + self.kernel_info_timeout, give_up)
|
||||||
|
# actually wait for it
|
||||||
|
yield future
|
||||||
|
|
||||||
@gen.coroutine
|
@gen.coroutine
|
||||||
def get(self, kernel_id):
|
def get(self, kernel_id):
|
||||||
self.kernel_id = cast_unicode(kernel_id, 'ascii')
|
self.kernel_id = cast_unicode(kernel_id, 'ascii')
|
||||||
yield self.request_kernel_info()
|
yield super(ZMQChannelHandler, self).get(kernel_id=kernel_id)
|
||||||
super(ZMQChannelHandler, self).get(kernel_id)
|
|
||||||
|
|
||||||
def open(self, kernel_id):
|
def open(self, kernel_id):
|
||||||
super(ZMQChannelHandler, self).open()
|
super(ZMQChannelHandler, self).open()
|
||||||
|
Loading…
Reference in New Issue
Block a user