diff --git a/IPython/html/base/zmqhandlers.py b/IPython/html/base/zmqhandlers.py
index 3e3f45123..d69155bd4 100644
--- a/IPython/html/base/zmqhandlers.py
+++ b/IPython/html/base/zmqhandlers.py
@@ -17,6 +17,7 @@ except ImportError:
import logging
import tornado
+from tornado import ioloop
from tornado import web
from tornado import websocket
@@ -103,8 +104,12 @@ class ZMQStreamHandler(websocket.WebSocketHandler):
"""
return True
+# ping interval for keeping websockets alive (30 seconds)
+WS_PING_INTERVAL = 30000
class AuthenticatedZMQStreamHandler(ZMQStreamHandler, IPythonHandler):
+ ping_callback = None
+
def set_default_headers(self):
"""Undo the set_default_headers in IPythonHandler
@@ -124,6 +129,16 @@ class AuthenticatedZMQStreamHandler(ZMQStreamHandler, IPythonHandler):
self.session = Session(config=self.config)
self.save_on_message = self.on_message
self.on_message = self.on_first_message
+ self.ping_callback = ioloop.PeriodicCallback(self.send_ping, WS_PING_INTERVAL)
+ self.ping_callback.start()
+
+ def send_ping(self):
+ """send a ping to keep the websocket alive"""
+ if self.stream.closed() and self.ping_callback is not None:
+ self.ping_callback.stop()
+ return
+
+ self.ping(b'')
def _inject_cookie_message(self, msg):
"""Inject the first message, which is the document cookie,