##// END OF EJS Templates
WebSocket url is now passed to browser when a kernel is started.
Brian E. Granger -
Show More
@@ -61,8 +61,10 b' class MainKernelHandler(web.RequestHandler):'
61 km = self.application.kernel_manager
61 km = self.application.kernel_manager
62 notebook_id = self.get_argument('notebook', default=None)
62 notebook_id = self.get_argument('notebook', default=None)
63 kernel_id = km.start_kernel(notebook_id)
63 kernel_id = km.start_kernel(notebook_id)
64 ws_url = self.application.ipython_app.get_ws_url()
65 data = {'ws_url':ws_url,'kernel_id':kernel_id}
64 self.set_header('Location', '/'+kernel_id)
66 self.set_header('Location', '/'+kernel_id)
65 self.finish(jsonapi.dumps(kernel_id))
67 self.finish(jsonapi.dumps(data))
66
68
67
69
68 class KernelHandler(web.RequestHandler):
70 class KernelHandler(web.RequestHandler):
@@ -85,7 +87,10 b' class KernelActionHandler(web.RequestHandler):'
85 self.set_status(204)
87 self.set_status(204)
86 if action == 'restart':
88 if action == 'restart':
87 new_kernel_id = km.restart_kernel(kernel_id)
89 new_kernel_id = km.restart_kernel(kernel_id)
88 self.write(jsonapi.dumps(new_kernel_id))
90 ws_url = self.application.ipython_app.get_ws_url()
91 data = {'ws_url':ws_url,'kernel_id':new_kernel_id}
92 self.set_header('Location', '/'+new_kernel_id)
93 self.write(jsonapi.dumps(data))
89 self.finish()
94 self.finish()
90
95
91
96
@@ -126,21 +131,36 b' class IOPubHandler(ZMQStreamHandler):'
126 def initialize(self, *args, **kwargs):
131 def initialize(self, *args, **kwargs):
127 self._kernel_alive = True
132 self._kernel_alive = True
128 self._beating = False
133 self._beating = False
134 self.iopub_stream = None
135 self.hb_stream = None
129
136
130 def open(self, kernel_id):
137 def open(self, kernel_id):
131 km = self.application.kernel_manager
138 km = self.application.kernel_manager
132 self.kernel_id = kernel_id
139 self.kernel_id = kernel_id
133 self.session = Session()
140 self.session = Session()
134 self.time_to_dead = km.time_to_dead
141 self.time_to_dead = km.time_to_dead
135 self.iopub_stream = km.create_iopub_stream(kernel_id)
142 try:
136 self.hb_stream = km.create_hb_stream(kernel_id)
143 self.iopub_stream = km.create_iopub_stream(kernel_id)
137 self.iopub_stream.on_recv(self._on_zmq_reply)
144 self.hb_stream = km.create_hb_stream(kernel_id)
138 self.start_hb(self.kernel_died)
145 except web.HTTPError:
146 # WebSockets don't response to traditional error codes so we
147 # close the connection.
148 if not self.stream.closed():
149 self.stream.close()
150 else:
151 self.iopub_stream.on_recv(self._on_zmq_reply)
152 self.start_hb(self.kernel_died)
139
153
140 def on_close(self):
154 def on_close(self):
155 # This method can be called twice, once by self.kernel_died and once
156 # from the WebSocket close event. If the WebSocket connection is
157 # closed before the ZMQ streams are setup, they could be None.
141 self.stop_hb()
158 self.stop_hb()
142 self.iopub_stream.close()
159 if self.iopub_stream is not None and not self.iopub_stream.closed():
143 self.hb_stream.close()
160 self.iopub_stream.on_recv(None)
161 self.iopub_stream.close()
162 if self.hb_stream is not None and not self.hb_stream.closed():
163 self.hb_stream.close()
144
164
145 def start_hb(self, callback):
165 def start_hb(self, callback):
146 """Start the heartbeating and call the callback if the kernel dies."""
166 """Start the heartbeating and call the callback if the kernel dies."""
@@ -188,15 +208,22 b' class IOPubHandler(ZMQStreamHandler):'
188 class ShellHandler(ZMQStreamHandler):
208 class ShellHandler(ZMQStreamHandler):
189
209
190 def initialize(self, *args, **kwargs):
210 def initialize(self, *args, **kwargs):
191 pass
211 self.shell_stream = None
192
212
193 def open(self, kernel_id):
213 def open(self, kernel_id):
194 km = self.application.kernel_manager
214 km = self.application.kernel_manager
195 self.max_msg_size = km.max_msg_size
215 self.max_msg_size = km.max_msg_size
196 self.kernel_id = kernel_id
216 self.kernel_id = kernel_id
197 self.session = Session()
217 try:
198 self.shell_stream = self.application.kernel_manager.create_shell_stream(kernel_id)
218 self.shell_stream = km.create_shell_stream(kernel_id)
199 self.shell_stream.on_recv(self._on_zmq_reply)
219 except web.HTTPError:
220 # WebSockets don't response to traditional error codes so we
221 # close the connection.
222 if not self.stream.closed():
223 self.stream.close()
224 else:
225 self.session = Session()
226 self.shell_stream.on_recv(self._on_zmq_reply)
200
227
201 def on_message(self, msg):
228 def on_message(self, msg):
202 if len(msg) < self.max_msg_size:
229 if len(msg) < self.max_msg_size:
@@ -204,7 +231,9 b' class ShellHandler(ZMQStreamHandler):'
204 self.session.send(self.shell_stream, msg)
231 self.session.send(self.shell_stream, msg)
205
232
206 def on_close(self):
233 def on_close(self):
207 self.shell_stream.close()
234 # Make sure the stream exists and is not already closed.
235 if self.shell_stream is not None and not self.shell_stream.closed():
236 self.shell_stream.close()
208
237
209
238
210 #-----------------------------------------------------------------------------
239 #-----------------------------------------------------------------------------
@@ -300,7 +300,21 b' class MappingKernelManager(KernelManager):'
300 # Now save the new kernel/notebook association. We have to save it
300 # Now save the new kernel/notebook association. We have to save it
301 # after the old kernel is killed as that will delete the mapping.
301 # after the old kernel is killed as that will delete the mapping.
302 self.set_kernel_for_notebook(notebook_id, new_kernel_id)
302 self.set_kernel_for_notebook(notebook_id, new_kernel_id)
303 self.log.debug("Kernel restarted: %s" % new_kernel_id)
303 self.log.info("Kernel restarted: %s" % new_kernel_id)
304 return new_kernel_id
304 return new_kernel_id
305
305
306 def create_iopub_stream(self, kernel_id):
307 if kernel_id not in self:
308 raise web.HTTPError(404)
309 return super(MappingKernelManager, self).create_iopub_stream(kernel_id)
310
311 def create_shell_stream(self, kernel_id):
312 if kernel_id not in self:
313 raise web.HTTPError(404)
314 return super(MappingKernelManager, self).create_shell_stream(kernel_id)
315
316 def create_hb_stream(self, kernel_id):
317 if kernel_id not in self:
318 raise web.HTTPError(404)
319 return super(MappingKernelManager, self).create_hb_stream(kernel_id)
306
320
@@ -72,7 +72,7 b' ipython notebook --port=5555 --ip=* # Listen on port 5555, all interfaces'
72
72
73 class NotebookWebApplication(web.Application):
73 class NotebookWebApplication(web.Application):
74
74
75 def __init__(self, kernel_manager, notebook_manager, log):
75 def __init__(self, ipython_app, kernel_manager, notebook_manager, log):
76 handlers = [
76 handlers = [
77 (r"/", NBBrowserHandler),
77 (r"/", NBBrowserHandler),
78 (r"/new", NewHandler),
78 (r"/new", NewHandler),
@@ -95,6 +95,7 b' class NotebookWebApplication(web.Application):'
95 self.kernel_manager = kernel_manager
95 self.kernel_manager = kernel_manager
96 self.log = log
96 self.log = log
97 self.notebook_manager = notebook_manager
97 self.notebook_manager = notebook_manager
98 self.ipython_app = ipython_app
98
99
99
100
100 #-----------------------------------------------------------------------------
101 #-----------------------------------------------------------------------------
@@ -115,6 +116,7 b' aliases.update({'
115 'port': 'IPythonNotebookApp.port',
116 'port': 'IPythonNotebookApp.port',
116 'keyfile': 'IPythonNotebookApp.keyfile',
117 'keyfile': 'IPythonNotebookApp.keyfile',
117 'certfile': 'IPythonNotebookApp.certfile',
118 'certfile': 'IPythonNotebookApp.certfile',
119 'ws-hostname': 'IPythonNotebookApp.ws_hostname',
118 'notebook-dir': 'NotebookManager.notebook_dir'
120 'notebook-dir': 'NotebookManager.notebook_dir'
119 })
121 })
120
122
@@ -160,6 +162,13 b' class IPythonNotebookApp(BaseIPythonApplication):'
160 help="The port the notebook server will listen on."
162 help="The port the notebook server will listen on."
161 )
163 )
162
164
165 ws_hostname = Unicode(LOCALHOST, config=True,
166 help="""The FQDN or IP for WebSocket connections. The default will work
167 fine when the server is listening on localhost, but this needs to
168 be set if the ip option is used. It will be used as the hostname part
169 of the WebSocket url: ws://hostname/path."""
170 )
171
163 certfile = Unicode(u'', config=True,
172 certfile = Unicode(u'', config=True,
164 help="""The full path to an SSL/TLS certificate file."""
173 help="""The full path to an SSL/TLS certificate file."""
165 )
174 )
@@ -168,6 +177,14 b' class IPythonNotebookApp(BaseIPythonApplication):'
168 help="""The full path to a private key file for usage with SSL/TLS."""
177 help="""The full path to a private key file for usage with SSL/TLS."""
169 )
178 )
170
179
180 def get_ws_url(self):
181 """Return the WebSocket URL for this server."""
182 if self.certfile:
183 prefix = u'wss://'
184 else:
185 prefix = u'ws://'
186 return prefix + self.ws_hostname + u':' + unicode(self.port)
187
171 def parse_command_line(self, argv=None):
188 def parse_command_line(self, argv=None):
172 super(IPythonNotebookApp, self).parse_command_line(argv)
189 super(IPythonNotebookApp, self).parse_command_line(argv)
173 if argv is None:
190 if argv is None:
@@ -202,7 +219,7 b' class IPythonNotebookApp(BaseIPythonApplication):'
202 super(IPythonNotebookApp, self).initialize(argv)
219 super(IPythonNotebookApp, self).initialize(argv)
203 self.init_configurables()
220 self.init_configurables()
204 self.web_app = NotebookWebApplication(
221 self.web_app = NotebookWebApplication(
205 self.kernel_manager, self.notebook_manager, self.log
222 self, self.kernel_manager, self.notebook_manager, self.log
206 )
223 )
207 if self.certfile:
224 if self.certfile:
208 ssl_options = dict(certfile=self.certfile)
225 ssl_options = dict(certfile=self.certfile)
@@ -61,9 +61,10 b' var IPython = (function (IPython) {'
61 };
61 };
62
62
63
63
64 Kernel.prototype._handle_start_kernel = function (kernel_id, callback) {
64 Kernel.prototype._handle_start_kernel = function (json, callback) {
65 this.running = true;
65 this.running = true;
66 this.kernel_id = kernel_id;
66 this.kernel_id = json.kernel_id;
67 this.ws_url = json.ws_url;
67 this.kernel_url = this.base_url + "/" + this.kernel_id;
68 this.kernel_url = this.base_url + "/" + this.kernel_id;
68 this.start_channels();
69 this.start_channels();
69 callback();
70 callback();
@@ -73,7 +74,8 b' var IPython = (function (IPython) {'
73
74
74 Kernel.prototype.start_channels = function () {
75 Kernel.prototype.start_channels = function () {
75 this.stop_channels();
76 this.stop_channels();
76 var ws_url = "ws://127.0.0.1:8888" + this.kernel_url;
77 var ws_url = this.ws_url + this.kernel_url;
78 console.log("Starting WS:", ws_url);
77 this.shell_channel = new WebSocket(ws_url + "/shell");
79 this.shell_channel = new WebSocket(ws_url + "/shell");
78 this.iopub_channel = new WebSocket(ws_url + "/iopub");
80 this.iopub_channel = new WebSocket(ws_url + "/iopub");
79 };
81 };
General Comments 0
You need to be logged in to leave comments. Login now