##// END OF EJS Templates
Merge branch 'enh/httpauth' of https://github.com/satra/ipython into satra-enh/httpauth
Brian E. Granger -
r4723:06032c55 merge
parent child Browse files
Show More
@@ -0,0 +1,66
1 <!DOCTYPE HTML>
2 <html>
3
4 <head>
5 <meta charset="utf-8">
6
7 <title>IPython Notebook</title>
8
9 <link rel="stylesheet" href="static/jquery/css/themes/aristo/jquery-wijmo.css" type="text/css" />
10 <!-- <link rel="stylesheet" href="static/jquery/css/themes/rocket/jquery-wijmo.css" type="text/css" /> -->
11 <!-- <link rel="stylesheet" href="static/jquery/css/themes/smoothness/jquery-ui-1.8.14.custom.css" type="text/css" />-->
12
13 <link rel="stylesheet" href="static/css/boilerplate.css" type="text/css" />
14 <link rel="stylesheet" href="static/css/layout.css" type="text/css" />
15 <link rel="stylesheet" href="static/css/base.css" type="text/css" />
16 <script type="text/javascript" charset="utf-8">
17 function add_next_to_action(){
18 // add 'next' argument to action url, to preserve redirect
19 var query = location.search.substring(1);
20 var form = document.forms[0];
21 var action = form.getAttribute("action");
22 form.setAttribute("action", action + '?' + query);
23 }
24 </script>
25 </head>
26
27 <body onload="add_next_to_action()">
28
29 <div id="header">
30 <span id="ipython_notebook"><h1>IPython Notebook</h1></span>
31 </div>
32
33 <div id="header_border"></div>
34
35 <div id="main_app">
36
37 <div id="app_hbox">
38
39 <div id="left_panel">
40 </div>
41
42 <div id="content_panel">
43 <form action="/login" method="post">
44 Name: <input type="text" name="name" value="{{user_id}}">
45 Password: <input type="password" name="password">
46 <input type="submit" value="Sign in">
47 </form>
48 </div>
49 <div id="right_panel">
50 </div>
51
52 </div>
53
54 </div>
55
56 <script src="static/jquery/js/jquery-1.6.2.min.js" type="text/javascript" charset="utf-8"></script>
57 <script src="static/jquery/js/jquery-ui-1.8.14.custom.min.js" type="text/javascript" charset="utf-8"></script>
58 <script src="static/js/namespace.js" type="text/javascript" charset="utf-8"></script>
59 <script src="static/js/notebooklist.js" type="text/javascript" charset="utf-8"></script>
60 <script src="static/js/nbbrowser_main.js" type="text/javascript" charset="utf-8"></script>
61
62 </body>
63
64 </html>
65
66
@@ -1,334 +1,419
1 """Tornado handlers for the notebook.
1 """Tornado handlers for the notebook.
2
2
3 Authors:
3 Authors:
4
4
5 * Brian Granger
5 * Brian Granger
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2008-2011 The IPython Development Team
9 # Copyright (C) 2008-2011 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 import logging
20 import Cookie
21
19 from tornado import web
22 from tornado import web
20 from tornado import websocket
23 from tornado import websocket
21
24
22 from zmq.eventloop import ioloop
25 from zmq.eventloop import ioloop
23 from zmq.utils import jsonapi
26 from zmq.utils import jsonapi
24
27
25 from IPython.zmq.session import Session
28 from IPython.zmq.session import Session
26
29
27 try:
30 try:
28 from docutils.core import publish_string
31 from docutils.core import publish_string
29 except ImportError:
32 except ImportError:
30 publish_string = None
33 publish_string = None
31
34
32
35
33
36
34 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
35 # Top-level handlers
38 # Top-level handlers
36 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
37
40
38
41 class AuthenticatedHandler(web.RequestHandler):
39 class NBBrowserHandler(web.RequestHandler):
42 """A RequestHandler with an authenticated user."""
43 def get_current_user(self):
44 password = self.get_secure_cookie("password")
45 if password is None:
46 # cookie doesn't exist, or is invalid. Clear to prevent repeated
47 # 'Invalid cookie signature' warnings.
48 self.clear_cookie('password')
49 self.clear_cookie("user_id")
50 if self.application.password and self.application.password != password:
51 return None
52 return self.get_secure_cookie("user") or 'anonymous'
53
54 class NBBrowserHandler(AuthenticatedHandler):
55 @web.authenticated
40 def get(self):
56 def get(self):
41 nbm = self.application.notebook_manager
57 nbm = self.application.notebook_manager
42 project = nbm.notebook_dir
58 project = nbm.notebook_dir
43 self.render('nbbrowser.html', project=project)
59 self.render('nbbrowser.html', project=project)
44
60
61 class LoginHandler(AuthenticatedHandler):
62 def get(self):
63 user_id = self.get_secure_cookie("user") or ''
64 self.render('login.html', user_id=user_id)
65
66 def post(self):
67 self.set_secure_cookie("user", self.get_argument("name", default=u''))
68 self.set_secure_cookie("password", self.get_argument("password", default=u''))
69 url = self.get_argument("next", default="/")
70 self.redirect(url)
45
71
46 class NewHandler(web.RequestHandler):
72 class NewHandler(AuthenticatedHandler):
73 @web.authenticated
47 def get(self):
74 def get(self):
48 notebook_id = self.application.notebook_manager.new_notebook()
75 notebook_id = self.application.notebook_manager.new_notebook()
49 self.render('notebook.html', notebook_id=notebook_id)
76 self.render('notebook.html', notebook_id=notebook_id)
50
77
51
78
52 class NamedNotebookHandler(web.RequestHandler):
79 class NamedNotebookHandler(AuthenticatedHandler):
80 @web.authenticated
53 def get(self, notebook_id):
81 def get(self, notebook_id):
54 nbm = self.application.notebook_manager
82 nbm = self.application.notebook_manager
55 if not nbm.notebook_exists(notebook_id):
83 if not nbm.notebook_exists(notebook_id):
56 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
84 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
57 self.render('notebook.html', notebook_id=notebook_id)
85 self.render('notebook.html', notebook_id=notebook_id)
58
86
59
87
60 #-----------------------------------------------------------------------------
88 #-----------------------------------------------------------------------------
61 # Kernel handlers
89 # Kernel handlers
62 #-----------------------------------------------------------------------------
90 #-----------------------------------------------------------------------------
63
91
64
92
65 class MainKernelHandler(web.RequestHandler):
93 class MainKernelHandler(AuthenticatedHandler):
66
94
95 @web.authenticated
67 def get(self):
96 def get(self):
68 km = self.application.kernel_manager
97 km = self.application.kernel_manager
69 self.finish(jsonapi.dumps(km.kernel_ids))
98 self.finish(jsonapi.dumps(km.kernel_ids))
70
99
100 @web.authenticated
71 def post(self):
101 def post(self):
72 km = self.application.kernel_manager
102 km = self.application.kernel_manager
73 notebook_id = self.get_argument('notebook', default=None)
103 notebook_id = self.get_argument('notebook', default=None)
74 kernel_id = km.start_kernel(notebook_id)
104 kernel_id = km.start_kernel(notebook_id)
75 ws_url = self.application.ipython_app.get_ws_url()
105 ws_url = self.application.ipython_app.get_ws_url()
76 data = {'ws_url':ws_url,'kernel_id':kernel_id}
106 data = {'ws_url':ws_url,'kernel_id':kernel_id}
77 self.set_header('Location', '/'+kernel_id)
107 self.set_header('Location', '/'+kernel_id)
78 self.finish(jsonapi.dumps(data))
108 self.finish(jsonapi.dumps(data))
79
109
80
110
81 class KernelHandler(web.RequestHandler):
111 class KernelHandler(AuthenticatedHandler):
82
112
83 SUPPORTED_METHODS = ('DELETE')
113 SUPPORTED_METHODS = ('DELETE')
84
114
115 @web.authenticated
85 def delete(self, kernel_id):
116 def delete(self, kernel_id):
86 km = self.application.kernel_manager
117 km = self.application.kernel_manager
87 km.kill_kernel(kernel_id)
118 km.kill_kernel(kernel_id)
88 self.set_status(204)
119 self.set_status(204)
89 self.finish()
120 self.finish()
90
121
91
122
92 class KernelActionHandler(web.RequestHandler):
123 class KernelActionHandler(AuthenticatedHandler):
93
124
125 @web.authenticated
94 def post(self, kernel_id, action):
126 def post(self, kernel_id, action):
95 km = self.application.kernel_manager
127 km = self.application.kernel_manager
96 if action == 'interrupt':
128 if action == 'interrupt':
97 km.interrupt_kernel(kernel_id)
129 km.interrupt_kernel(kernel_id)
98 self.set_status(204)
130 self.set_status(204)
99 if action == 'restart':
131 if action == 'restart':
100 new_kernel_id = km.restart_kernel(kernel_id)
132 new_kernel_id = km.restart_kernel(kernel_id)
101 ws_url = self.application.ipython_app.get_ws_url()
133 ws_url = self.application.ipython_app.get_ws_url()
102 data = {'ws_url':ws_url,'kernel_id':new_kernel_id}
134 data = {'ws_url':ws_url,'kernel_id':new_kernel_id}
103 self.set_header('Location', '/'+new_kernel_id)
135 self.set_header('Location', '/'+new_kernel_id)
104 self.write(jsonapi.dumps(data))
136 self.write(jsonapi.dumps(data))
105 self.finish()
137 self.finish()
106
138
107
139
108 class ZMQStreamHandler(websocket.WebSocketHandler):
140 class ZMQStreamHandler(websocket.WebSocketHandler):
109
141
110 def _reserialize_reply(self, msg_list):
142 def _reserialize_reply(self, msg_list):
111 """Reserialize a reply message using JSON.
143 """Reserialize a reply message using JSON.
112
144
113 This takes the msg list from the ZMQ socket, unserializes it using
145 This takes the msg list from the ZMQ socket, unserializes it using
114 self.session and then serializes the result using JSON. This method
146 self.session and then serializes the result using JSON. This method
115 should be used by self._on_zmq_reply to build messages that can
147 should be used by self._on_zmq_reply to build messages that can
116 be sent back to the browser.
148 be sent back to the browser.
117 """
149 """
118 idents, msg_list = self.session.feed_identities(msg_list)
150 idents, msg_list = self.session.feed_identities(msg_list)
119 msg = self.session.unserialize(msg_list)
151 msg = self.session.unserialize(msg_list)
120 try:
152 try:
121 msg['header'].pop('date')
153 msg['header'].pop('date')
122 except KeyError:
154 except KeyError:
123 pass
155 pass
124 try:
156 try:
125 msg['parent_header'].pop('date')
157 msg['parent_header'].pop('date')
126 except KeyError:
158 except KeyError:
127 pass
159 pass
128 msg.pop('buffers')
160 msg.pop('buffers')
129 return jsonapi.dumps(msg)
161 return jsonapi.dumps(msg)
130
162
131 def _on_zmq_reply(self, msg_list):
163 def _on_zmq_reply(self, msg_list):
132 try:
164 try:
133 msg = self._reserialize_reply(msg_list)
165 msg = self._reserialize_reply(msg_list)
134 except:
166 except:
135 self.application.kernel_manager.log.critical("Malformed message: %r" % msg_list)
167 self.application.kernel_manager.log.critical("Malformed message: %r" % msg_list)
136 else:
168 else:
137 self.write_message(msg)
169 self.write_message(msg)
138
170
139
171 class AuthenticatedZMQStreamHandler(ZMQStreamHandler):
140 class IOPubHandler(ZMQStreamHandler):
172 def open(self, kernel_id):
173 self.kernel_id = kernel_id
174 self.session = Session()
175 self.save_on_message = self.on_message
176 self.on_message = self.on_first_message
177
178 def get_current_user(self):
179 password = self.get_secure_cookie("password")
180 if password is None:
181 # clear cookies, to prevent future Invalid cookie signature warnings
182 self._cookies = Cookie.SimpleCookie()
183 if self.application.password and self.application.password != password:
184 return None
185 return self.get_secure_cookie("user") or 'anonymous'
186
187 def _inject_cookie_message(self, msg):
188 """Inject the first message, which is the document cookie,
189 for authentication."""
190 if isinstance(msg, unicode):
191 # Cookie can't constructor doesn't accept unicode strings for some reason
192 msg = msg.encode('utf8', 'replace')
193 try:
194 self._cookies = Cookie.SimpleCookie(msg)
195 except:
196 logging.warn("couldn't parse cookie string: %s",msg, exc_info=True)
197
198 def on_first_message(self, msg):
199 self._inject_cookie_message(msg)
200 if self.get_current_user() is None:
201 logging.warn("Couldn't authenticate WebSocket connection")
202 raise web.HTTPError(403)
203 self.on_message = self.save_on_message
204
205
206 class IOPubHandler(AuthenticatedZMQStreamHandler):
141
207
142 def initialize(self, *args, **kwargs):
208 def initialize(self, *args, **kwargs):
143 self._kernel_alive = True
209 self._kernel_alive = True
144 self._beating = False
210 self._beating = False
145 self.iopub_stream = None
211 self.iopub_stream = None
146 self.hb_stream = None
212 self.hb_stream = None
147
213
148 def open(self, kernel_id):
214 def on_first_message(self, msg):
215 try:
216 super(IOPubHandler, self).on_first_message(msg)
217 except web.HTTPError:
218 self.close()
219 return
149 km = self.application.kernel_manager
220 km = self.application.kernel_manager
150 self.kernel_id = kernel_id
151 self.session = Session()
152 self.time_to_dead = km.time_to_dead
221 self.time_to_dead = km.time_to_dead
222 kernel_id = self.kernel_id
153 try:
223 try:
154 self.iopub_stream = km.create_iopub_stream(kernel_id)
224 self.iopub_stream = km.create_iopub_stream(kernel_id)
155 self.hb_stream = km.create_hb_stream(kernel_id)
225 self.hb_stream = km.create_hb_stream(kernel_id)
156 except web.HTTPError:
226 except web.HTTPError:
157 # WebSockets don't response to traditional error codes so we
227 # WebSockets don't response to traditional error codes so we
158 # close the connection.
228 # close the connection.
159 if not self.stream.closed():
229 if not self.stream.closed():
160 self.stream.close()
230 self.stream.close()
231 self.close()
161 else:
232 else:
162 self.iopub_stream.on_recv(self._on_zmq_reply)
233 self.iopub_stream.on_recv(self._on_zmq_reply)
163 self.start_hb(self.kernel_died)
234 self.start_hb(self.kernel_died)
235
236 def on_message(self, msg):
237 pass
164
238
165 def on_close(self):
239 def on_close(self):
166 # This method can be called twice, once by self.kernel_died and once
240 # This method can be called twice, once by self.kernel_died and once
167 # from the WebSocket close event. If the WebSocket connection is
241 # from the WebSocket close event. If the WebSocket connection is
168 # closed before the ZMQ streams are setup, they could be None.
242 # closed before the ZMQ streams are setup, they could be None.
169 self.stop_hb()
243 self.stop_hb()
170 if self.iopub_stream is not None and not self.iopub_stream.closed():
244 if self.iopub_stream is not None and not self.iopub_stream.closed():
171 self.iopub_stream.on_recv(None)
245 self.iopub_stream.on_recv(None)
172 self.iopub_stream.close()
246 self.iopub_stream.close()
173 if self.hb_stream is not None and not self.hb_stream.closed():
247 if self.hb_stream is not None and not self.hb_stream.closed():
174 self.hb_stream.close()
248 self.hb_stream.close()
175
249
176 def start_hb(self, callback):
250 def start_hb(self, callback):
177 """Start the heartbeating and call the callback if the kernel dies."""
251 """Start the heartbeating and call the callback if the kernel dies."""
178 if not self._beating:
252 if not self._beating:
179 self._kernel_alive = True
253 self._kernel_alive = True
180
254
181 def ping_or_dead():
255 def ping_or_dead():
182 if self._kernel_alive:
256 if self._kernel_alive:
183 self._kernel_alive = False
257 self._kernel_alive = False
184 self.hb_stream.send(b'ping')
258 self.hb_stream.send(b'ping')
185 else:
259 else:
186 try:
260 try:
187 callback()
261 callback()
188 except:
262 except:
189 pass
263 pass
190 finally:
264 finally:
191 self._hb_periodic_callback.stop()
265 self._hb_periodic_callback.stop()
192
266
193 def beat_received(msg):
267 def beat_received(msg):
194 self._kernel_alive = True
268 self._kernel_alive = True
195
269
196 self.hb_stream.on_recv(beat_received)
270 self.hb_stream.on_recv(beat_received)
197 self._hb_periodic_callback = ioloop.PeriodicCallback(ping_or_dead, self.time_to_dead*1000)
271 self._hb_periodic_callback = ioloop.PeriodicCallback(ping_or_dead, self.time_to_dead*1000)
198 self._hb_periodic_callback.start()
272 self._hb_periodic_callback.start()
199 self._beating= True
273 self._beating= True
200
274
201 def stop_hb(self):
275 def stop_hb(self):
202 """Stop the heartbeating and cancel all related callbacks."""
276 """Stop the heartbeating and cancel all related callbacks."""
203 if self._beating:
277 if self._beating:
204 self._hb_periodic_callback.stop()
278 self._hb_periodic_callback.stop()
205 if not self.hb_stream.closed():
279 if not self.hb_stream.closed():
206 self.hb_stream.on_recv(None)
280 self.hb_stream.on_recv(None)
207
281
208 def kernel_died(self):
282 def kernel_died(self):
209 self.application.kernel_manager.delete_mapping_for_kernel(self.kernel_id)
283 self.application.kernel_manager.delete_mapping_for_kernel(self.kernel_id)
210 self.write_message(
284 self.write_message(
211 {'header': {'msg_type': 'status'},
285 {'header': {'msg_type': 'status'},
212 'parent_header': {},
286 'parent_header': {},
213 'content': {'execution_state':'dead'}
287 'content': {'execution_state':'dead'}
214 }
288 }
215 )
289 )
216 self.on_close()
290 self.on_close()
217
291
218
292
219 class ShellHandler(ZMQStreamHandler):
293 class ShellHandler(AuthenticatedZMQStreamHandler):
220
294
221 def initialize(self, *args, **kwargs):
295 def initialize(self, *args, **kwargs):
222 self.shell_stream = None
296 self.shell_stream = None
223
297
224 def open(self, kernel_id):
298 def on_first_message(self, msg):
299 try:
300 super(ShellHandler, self).on_first_message(msg)
301 except web.HTTPError:
302 self.close()
303 return
225 km = self.application.kernel_manager
304 km = self.application.kernel_manager
226 self.max_msg_size = km.max_msg_size
305 self.max_msg_size = km.max_msg_size
227 self.kernel_id = kernel_id
306 kernel_id = self.kernel_id
228 try:
307 try:
229 self.shell_stream = km.create_shell_stream(kernel_id)
308 self.shell_stream = km.create_shell_stream(kernel_id)
230 except web.HTTPError:
309 except web.HTTPError:
231 # WebSockets don't response to traditional error codes so we
310 # WebSockets don't response to traditional error codes so we
232 # close the connection.
311 # close the connection.
233 if not self.stream.closed():
312 if not self.stream.closed():
234 self.stream.close()
313 self.stream.close()
314 self.close()
235 else:
315 else:
236 self.session = Session()
237 self.shell_stream.on_recv(self._on_zmq_reply)
316 self.shell_stream.on_recv(self._on_zmq_reply)
238
317
239 def on_message(self, msg):
318 def on_message(self, msg):
240 if len(msg) < self.max_msg_size:
319 if len(msg) < self.max_msg_size:
241 msg = jsonapi.loads(msg)
320 msg = jsonapi.loads(msg)
242 self.session.send(self.shell_stream, msg)
321 self.session.send(self.shell_stream, msg)
243
322
244 def on_close(self):
323 def on_close(self):
245 # Make sure the stream exists and is not already closed.
324 # Make sure the stream exists and is not already closed.
246 if self.shell_stream is not None and not self.shell_stream.closed():
325 if self.shell_stream is not None and not self.shell_stream.closed():
247 self.shell_stream.close()
326 self.shell_stream.close()
248
327
249
328
250 #-----------------------------------------------------------------------------
329 #-----------------------------------------------------------------------------
251 # Notebook web service handlers
330 # Notebook web service handlers
252 #-----------------------------------------------------------------------------
331 #-----------------------------------------------------------------------------
253
332
254 class NotebookRootHandler(web.RequestHandler):
333 class NotebookRootHandler(AuthenticatedHandler):
255
334
335 @web.authenticated
256 def get(self):
336 def get(self):
257 nbm = self.application.notebook_manager
337 nbm = self.application.notebook_manager
258 files = nbm.list_notebooks()
338 files = nbm.list_notebooks()
259 self.finish(jsonapi.dumps(files))
339 self.finish(jsonapi.dumps(files))
260
340
341 @web.authenticated
261 def post(self):
342 def post(self):
262 nbm = self.application.notebook_manager
343 nbm = self.application.notebook_manager
263 body = self.request.body.strip()
344 body = self.request.body.strip()
264 format = self.get_argument('format', default='json')
345 format = self.get_argument('format', default='json')
265 name = self.get_argument('name', default=None)
346 name = self.get_argument('name', default=None)
266 if body:
347 if body:
267 notebook_id = nbm.save_new_notebook(body, name=name, format=format)
348 notebook_id = nbm.save_new_notebook(body, name=name, format=format)
268 else:
349 else:
269 notebook_id = nbm.new_notebook()
350 notebook_id = nbm.new_notebook()
270 self.set_header('Location', '/'+notebook_id)
351 self.set_header('Location', '/'+notebook_id)
271 self.finish(jsonapi.dumps(notebook_id))
352 self.finish(jsonapi.dumps(notebook_id))
272
353
273
354
274 class NotebookHandler(web.RequestHandler):
355 class NotebookHandler(AuthenticatedHandler):
275
356
276 SUPPORTED_METHODS = ('GET', 'PUT', 'DELETE')
357 SUPPORTED_METHODS = ('GET', 'PUT', 'DELETE')
277
358
359 @web.authenticated
278 def get(self, notebook_id):
360 def get(self, notebook_id):
279 nbm = self.application.notebook_manager
361 nbm = self.application.notebook_manager
280 format = self.get_argument('format', default='json')
362 format = self.get_argument('format', default='json')
281 last_mod, name, data = nbm.get_notebook(notebook_id, format)
363 last_mod, name, data = nbm.get_notebook(notebook_id, format)
282 if format == u'json':
364 if format == u'json':
283 self.set_header('Content-Type', 'application/json')
365 self.set_header('Content-Type', 'application/json')
284 self.set_header('Content-Disposition','attachment; filename="%s.ipynb"' % name)
366 self.set_header('Content-Disposition','attachment; filename="%s.ipynb"' % name)
285 elif format == u'py':
367 elif format == u'py':
286 self.set_header('Content-Type', 'application/x-python')
368 self.set_header('Content-Type', 'application/x-python')
287 self.set_header('Content-Disposition','attachment; filename="%s.py"' % name)
369 self.set_header('Content-Disposition','attachment; filename="%s.py"' % name)
288 self.set_header('Last-Modified', last_mod)
370 self.set_header('Last-Modified', last_mod)
289 self.finish(data)
371 self.finish(data)
290
372
373 @web.authenticated
291 def put(self, notebook_id):
374 def put(self, notebook_id):
292 nbm = self.application.notebook_manager
375 nbm = self.application.notebook_manager
293 format = self.get_argument('format', default='json')
376 format = self.get_argument('format', default='json')
294 name = self.get_argument('name', default=None)
377 name = self.get_argument('name', default=None)
295 nbm.save_notebook(notebook_id, self.request.body, name=name, format=format)
378 nbm.save_notebook(notebook_id, self.request.body, name=name, format=format)
296 self.set_status(204)
379 self.set_status(204)
297 self.finish()
380 self.finish()
298
381
382 @web.authenticated
299 def delete(self, notebook_id):
383 def delete(self, notebook_id):
300 nbm = self.application.notebook_manager
384 nbm = self.application.notebook_manager
301 nbm.delete_notebook(notebook_id)
385 nbm.delete_notebook(notebook_id)
302 self.set_status(204)
386 self.set_status(204)
303 self.finish()
387 self.finish()
304
388
305 #-----------------------------------------------------------------------------
389 #-----------------------------------------------------------------------------
306 # RST web service handlers
390 # RST web service handlers
307 #-----------------------------------------------------------------------------
391 #-----------------------------------------------------------------------------
308
392
309
393
310 class RSTHandler(web.RequestHandler):
394 class RSTHandler(AuthenticatedHandler):
311
395
396 @web.authenticated
312 def post(self):
397 def post(self):
313 if publish_string is None:
398 if publish_string is None:
314 raise web.HTTPError(503, u'docutils not available')
399 raise web.HTTPError(503, u'docutils not available')
315 body = self.request.body.strip()
400 body = self.request.body.strip()
316 source = body
401 source = body
317 # template_path=os.path.join(os.path.dirname(__file__), u'templates', u'rst_template.html')
402 # template_path=os.path.join(os.path.dirname(__file__), u'templates', u'rst_template.html')
318 defaults = {'file_insertion_enabled': 0,
403 defaults = {'file_insertion_enabled': 0,
319 'raw_enabled': 0,
404 'raw_enabled': 0,
320 '_disable_config': 1,
405 '_disable_config': 1,
321 'stylesheet_path': 0
406 'stylesheet_path': 0
322 # 'template': template_path
407 # 'template': template_path
323 }
408 }
324 try:
409 try:
325 html = publish_string(source, writer_name='html',
410 html = publish_string(source, writer_name='html',
326 settings_overrides=defaults
411 settings_overrides=defaults
327 )
412 )
328 except:
413 except:
329 raise web.HTTPError(400, u'Invalid RST')
414 raise web.HTTPError(400, u'Invalid RST')
330 print html
415 print html
331 self.set_header('Content-Type', 'text/html')
416 self.set_header('Content-Type', 'text/html')
332 self.finish(html)
417 self.finish(html)
333
418
334
419
@@ -1,280 +1,288
1 """A tornado based IPython notebook server.
1 """A tornado based IPython notebook server.
2
2
3 Authors:
3 Authors:
4
4
5 * Brian Granger
5 * Brian Granger
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2008-2011 The IPython Development Team
9 # Copyright (C) 2008-2011 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 import errno
19 import errno
20 import logging
20 import logging
21 import os
21 import os
22 import signal
22 import signal
23 import socket
23 import socket
24 import sys
24 import sys
25
25
26 import zmq
26 import zmq
27
27
28 # Install the pyzmq ioloop. This has to be done before anything else from
28 # Install the pyzmq ioloop. This has to be done before anything else from
29 # tornado is imported.
29 # tornado is imported.
30 from zmq.eventloop import ioloop
30 from zmq.eventloop import ioloop
31 import tornado.ioloop
31 import tornado.ioloop
32 tornado.ioloop = ioloop
32 tornado.ioloop = ioloop
33
33
34 from tornado import httpserver
34 from tornado import httpserver
35 from tornado import web
35 from tornado import web
36
36
37 from .kernelmanager import MappingKernelManager
37 from .kernelmanager import MappingKernelManager
38 from .handlers import (
38 from .handlers import (LoginHandler,
39 NBBrowserHandler, NewHandler, NamedNotebookHandler,
39 NBBrowserHandler, NewHandler, NamedNotebookHandler,
40 MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler,
40 MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler,
41 ShellHandler, NotebookRootHandler, NotebookHandler, RSTHandler
41 ShellHandler, NotebookRootHandler, NotebookHandler, RSTHandler
42 )
42 )
43 from .notebookmanager import NotebookManager
43 from .notebookmanager import NotebookManager
44
44
45 from IPython.core.application import BaseIPythonApplication
45 from IPython.core.application import BaseIPythonApplication
46 from IPython.core.profiledir import ProfileDir
46 from IPython.core.profiledir import ProfileDir
47 from IPython.zmq.session import Session
47 from IPython.zmq.session import Session
48 from IPython.zmq.zmqshell import ZMQInteractiveShell
48 from IPython.zmq.zmqshell import ZMQInteractiveShell
49 from IPython.zmq.ipkernel import (
49 from IPython.zmq.ipkernel import (
50 flags as ipkernel_flags,
50 flags as ipkernel_flags,
51 aliases as ipkernel_aliases,
51 aliases as ipkernel_aliases,
52 IPKernelApp
52 IPKernelApp
53 )
53 )
54 from IPython.utils.traitlets import Dict, Unicode, Int, List, Enum
54 from IPython.utils.traitlets import Dict, Unicode, Int, List, Enum
55
55
56 #-----------------------------------------------------------------------------
56 #-----------------------------------------------------------------------------
57 # Module globals
57 # Module globals
58 #-----------------------------------------------------------------------------
58 #-----------------------------------------------------------------------------
59
59
60 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
60 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
61 _kernel_action_regex = r"(?P<action>restart|interrupt)"
61 _kernel_action_regex = r"(?P<action>restart|interrupt)"
62 _notebook_id_regex = r"(?P<notebook_id>\w+-\w+-\w+-\w+-\w+)"
62 _notebook_id_regex = r"(?P<notebook_id>\w+-\w+-\w+-\w+-\w+)"
63
63
64 LOCALHOST = '127.0.0.1'
64 LOCALHOST = '127.0.0.1'
65
65
66 _examples = """
66 _examples = """
67 ipython notebook # start the notebook
67 ipython notebook # start the notebook
68 ipython notebook --profile=sympy # use the sympy profile
68 ipython notebook --profile=sympy # use the sympy profile
69 ipython notebook --pylab=inline # pylab in inline plotting mode
69 ipython notebook --pylab=inline # pylab in inline plotting mode
70 ipython notebook --certfile=mycert.pem # use SSL/TLS certificate
70 ipython notebook --certfile=mycert.pem # use SSL/TLS certificate
71 ipython notebook --port=5555 --ip=* # Listen on port 5555, all interfaces
71 ipython notebook --port=5555 --ip=* # Listen on port 5555, all interfaces
72 """
72 """
73
73
74 #-----------------------------------------------------------------------------
74 #-----------------------------------------------------------------------------
75 # The Tornado web application
75 # The Tornado web application
76 #-----------------------------------------------------------------------------
76 #-----------------------------------------------------------------------------
77
77
78 class NotebookWebApplication(web.Application):
78 class NotebookWebApplication(web.Application):
79
79
80 def __init__(self, ipython_app, kernel_manager, notebook_manager, log):
80 def __init__(self, ipython_app, kernel_manager, notebook_manager, log):
81 handlers = [
81 handlers = [
82 (r"/", NBBrowserHandler),
82 (r"/", NBBrowserHandler),
83 (r"/login", LoginHandler),
83 (r"/new", NewHandler),
84 (r"/new", NewHandler),
84 (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
85 (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
85 (r"/kernels", MainKernelHandler),
86 (r"/kernels", MainKernelHandler),
86 (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
87 (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
87 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
88 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
88 (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
89 (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
89 (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
90 (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
90 (r"/notebooks", NotebookRootHandler),
91 (r"/notebooks", NotebookRootHandler),
91 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
92 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
92 (r"/rstservice/render", RSTHandler)
93 (r"/rstservice/render", RSTHandler)
93 ]
94 ]
94 settings = dict(
95 settings = dict(
95 template_path=os.path.join(os.path.dirname(__file__), "templates"),
96 template_path=os.path.join(os.path.dirname(__file__), "templates"),
96 static_path=os.path.join(os.path.dirname(__file__), "static"),
97 static_path=os.path.join(os.path.dirname(__file__), "static"),
98 cookie_secret=os.urandom(1024),
99 login_url="/login",
97 )
100 )
98 web.Application.__init__(self, handlers, **settings)
101 web.Application.__init__(self, handlers, **settings)
99
102
100 self.kernel_manager = kernel_manager
103 self.kernel_manager = kernel_manager
101 self.log = log
104 self.log = log
102 self.notebook_manager = notebook_manager
105 self.notebook_manager = notebook_manager
103 self.ipython_app = ipython_app
106 self.ipython_app = ipython_app
104
107
105
108
106 #-----------------------------------------------------------------------------
109 #-----------------------------------------------------------------------------
107 # Aliases and Flags
110 # Aliases and Flags
108 #-----------------------------------------------------------------------------
111 #-----------------------------------------------------------------------------
109
112
110 flags = dict(ipkernel_flags)
113 flags = dict(ipkernel_flags)
111
114
112 # the flags that are specific to the frontend
115 # the flags that are specific to the frontend
113 # these must be scrubbed before being passed to the kernel,
116 # these must be scrubbed before being passed to the kernel,
114 # or it will raise an error on unrecognized flags
117 # or it will raise an error on unrecognized flags
115 notebook_flags = []
118 notebook_flags = []
116
119
117 aliases = dict(ipkernel_aliases)
120 aliases = dict(ipkernel_aliases)
118
121
119 aliases.update({
122 aliases.update({
120 'ip': 'IPythonNotebookApp.ip',
123 'ip': 'IPythonNotebookApp.ip',
121 'port': 'IPythonNotebookApp.port',
124 'port': 'IPythonNotebookApp.port',
122 'keyfile': 'IPythonNotebookApp.keyfile',
125 'keyfile': 'IPythonNotebookApp.keyfile',
123 'certfile': 'IPythonNotebookApp.certfile',
126 'certfile': 'IPythonNotebookApp.certfile',
124 'ws-hostname': 'IPythonNotebookApp.ws_hostname',
127 'ws-hostname': 'IPythonNotebookApp.ws_hostname',
125 'notebook-dir': 'NotebookManager.notebook_dir'
128 'notebook-dir': 'NotebookManager.notebook_dir',
126 })
129 })
127
130
128 notebook_aliases = [u'port', u'ip', u'keyfile', u'certfile', u'ws-hostname',
131 notebook_aliases = [u'port', u'ip', u'keyfile', u'certfile', u'ws-hostname',
129 u'notebook-dir']
132 u'notebook-dir']
130
133
131 #-----------------------------------------------------------------------------
134 #-----------------------------------------------------------------------------
132 # IPythonNotebookApp
135 # IPythonNotebookApp
133 #-----------------------------------------------------------------------------
136 #-----------------------------------------------------------------------------
134
137
135 class IPythonNotebookApp(BaseIPythonApplication):
138 class IPythonNotebookApp(BaseIPythonApplication):
136
139
137 name = 'ipython-notebook'
140 name = 'ipython-notebook'
138 default_config_file_name='ipython_notebook_config.py'
141 default_config_file_name='ipython_notebook_config.py'
139
142
140 description = """
143 description = """
141 The IPython HTML Notebook.
144 The IPython HTML Notebook.
142
145
143 This launches a Tornado based HTML Notebook Server that serves up an
146 This launches a Tornado based HTML Notebook Server that serves up an
144 HTML5/Javascript Notebook client.
147 HTML5/Javascript Notebook client.
145 """
148 """
146 examples = _examples
149 examples = _examples
147
150
148 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
151 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
149 MappingKernelManager, NotebookManager]
152 MappingKernelManager, NotebookManager]
150 flags = Dict(flags)
153 flags = Dict(flags)
151 aliases = Dict(aliases)
154 aliases = Dict(aliases)
152
155
153 kernel_argv = List(Unicode)
156 kernel_argv = List(Unicode)
154
157
155 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
158 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
156 default_value=logging.INFO,
159 default_value=logging.INFO,
157 config=True,
160 config=True,
158 help="Set the log level by value or name.")
161 help="Set the log level by value or name.")
159
162
160 # Network related information.
163 # Network related information.
161
164
162 ip = Unicode(LOCALHOST, config=True,
165 ip = Unicode(LOCALHOST, config=True,
163 help="The IP address the notebook server will listen on."
166 help="The IP address the notebook server will listen on."
164 )
167 )
165
168
166 def _ip_changed(self, name, old, new):
169 def _ip_changed(self, name, old, new):
167 if new == u'*': self.ip = u''
170 if new == u'*': self.ip = u''
168
171
169 port = Int(8888, config=True,
172 port = Int(8888, config=True,
170 help="The port the notebook server will listen on."
173 help="The port the notebook server will listen on."
171 )
174 )
172
175
173 ws_hostname = Unicode(LOCALHOST, config=True,
176 ws_hostname = Unicode(LOCALHOST, config=True,
174 help="""The FQDN or IP for WebSocket connections. The default will work
177 help="""The FQDN or IP for WebSocket connections. The default will work
175 fine when the server is listening on localhost, but this needs to
178 fine when the server is listening on localhost, but this needs to
176 be set if the ip option is used. It will be used as the hostname part
179 be set if the ip option is used. It will be used as the hostname part
177 of the WebSocket url: ws://hostname/path."""
180 of the WebSocket url: ws://hostname/path."""
178 )
181 )
179
182
180 certfile = Unicode(u'', config=True,
183 certfile = Unicode(u'', config=True,
181 help="""The full path to an SSL/TLS certificate file."""
184 help="""The full path to an SSL/TLS certificate file."""
182 )
185 )
183
186
184 keyfile = Unicode(u'', config=True,
187 keyfile = Unicode(u'', config=True,
185 help="""The full path to a private key file for usage with SSL/TLS."""
188 help="""The full path to a private key file for usage with SSL/TLS."""
186 )
189 )
187
190
191 password = Unicode(u'', config=True,
192 help="""Password to use for web authentication"""
193 )
194
188 def get_ws_url(self):
195 def get_ws_url(self):
189 """Return the WebSocket URL for this server."""
196 """Return the WebSocket URL for this server."""
190 if self.certfile:
197 if self.certfile:
191 prefix = u'wss://'
198 prefix = u'wss://'
192 else:
199 else:
193 prefix = u'ws://'
200 prefix = u'ws://'
194 return prefix + self.ws_hostname + u':' + unicode(self.port)
201 return prefix + self.ws_hostname + u':' + unicode(self.port)
195
202
196 def parse_command_line(self, argv=None):
203 def parse_command_line(self, argv=None):
197 super(IPythonNotebookApp, self).parse_command_line(argv)
204 super(IPythonNotebookApp, self).parse_command_line(argv)
198 if argv is None:
205 if argv is None:
199 argv = sys.argv[1:]
206 argv = sys.argv[1:]
200
207
201 self.kernel_argv = list(argv) # copy
208 self.kernel_argv = list(argv) # copy
202 # Kernel should inherit default config file from frontend
209 # Kernel should inherit default config file from frontend
203 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
210 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
204 # Scrub frontend-specific flags
211 # Scrub frontend-specific flags
205 for a in argv:
212 for a in argv:
206 if a.startswith('-') and a.lstrip('-') in notebook_flags:
213 if a.startswith('-') and a.lstrip('-') in notebook_flags:
207 self.kernel_argv.remove(a)
214 self.kernel_argv.remove(a)
208 for a in argv:
215 for a in argv:
209 if a.startswith('-'):
216 if a.startswith('-'):
210 alias = a.lstrip('-').split('=')[0]
217 alias = a.lstrip('-').split('=')[0]
211 if alias in notebook_aliases:
218 if alias in notebook_aliases:
212 self.kernel_argv.remove(a)
219 self.kernel_argv.remove(a)
213
220
214 def init_configurables(self):
221 def init_configurables(self):
215 # Don't let Qt or ZMQ swallow KeyboardInterupts.
222 # Don't let Qt or ZMQ swallow KeyboardInterupts.
216 signal.signal(signal.SIGINT, signal.SIG_DFL)
223 signal.signal(signal.SIGINT, signal.SIG_DFL)
217
224
218 # Create a KernelManager and start a kernel.
225 # Create a KernelManager and start a kernel.
219 self.kernel_manager = MappingKernelManager(
226 self.kernel_manager = MappingKernelManager(
220 config=self.config, log=self.log, kernel_argv=self.kernel_argv
227 config=self.config, log=self.log, kernel_argv=self.kernel_argv
221 )
228 )
222 self.notebook_manager = NotebookManager(config=self.config, log=self.log)
229 self.notebook_manager = NotebookManager(config=self.config, log=self.log)
223 self.notebook_manager.list_notebooks()
230 self.notebook_manager.list_notebooks()
224
231
225 def init_logging(self):
232 def init_logging(self):
226 super(IPythonNotebookApp, self).init_logging()
233 super(IPythonNotebookApp, self).init_logging()
227 # This prevents double log messages because tornado use a root logger that
234 # This prevents double log messages because tornado use a root logger that
228 # self.log is a child of. The logging module dipatches log messages to a log
235 # self.log is a child of. The logging module dipatches log messages to a log
229 # and all of its ancenstors until propagate is set to False.
236 # and all of its ancenstors until propagate is set to False.
230 self.log.propagate = False
237 self.log.propagate = False
231
238
232 def initialize(self, argv=None):
239 def initialize(self, argv=None):
233 super(IPythonNotebookApp, self).initialize(argv)
240 super(IPythonNotebookApp, self).initialize(argv)
234 self.init_configurables()
241 self.init_configurables()
235 self.web_app = NotebookWebApplication(
242 self.web_app = NotebookWebApplication(
236 self, self.kernel_manager, self.notebook_manager, self.log
243 self, self.kernel_manager, self.notebook_manager, self.log
237 )
244 )
238 if self.certfile:
245 if self.certfile:
239 ssl_options = dict(certfile=self.certfile)
246 ssl_options = dict(certfile=self.certfile)
240 if self.keyfile:
247 if self.keyfile:
241 ssl_options['keyfile'] = self.keyfile
248 ssl_options['keyfile'] = self.keyfile
242 else:
249 else:
243 ssl_options = None
250 ssl_options = None
251 self.web_app.password = self.password
244 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options)
252 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options)
245 if ssl_options is None and not self.ip:
253 if ssl_options is None and not self.ip:
246 self.log.critical('WARNING: the notebook server is listening on all IP addresses '
254 self.log.critical('WARNING: the notebook server is listening on all IP addresses '
247 'but not using any encryption or authentication. This is highly '
255 'but not using any encryption or authentication. This is highly '
248 'insecure and not recommended.')
256 'insecure and not recommended.')
249
257
250 # Try random ports centered around the default.
258 # Try random ports centered around the default.
251 from random import randint
259 from random import randint
252 n = 50 # Max number of attempts, keep reasonably large.
260 n = 50 # Max number of attempts, keep reasonably large.
253 for port in [self.port] + [self.port + randint(-2*n, 2*n) for i in range(n)]:
261 for port in [self.port] + [self.port + randint(-2*n, 2*n) for i in range(n)]:
254 try:
262 try:
255 self.http_server.listen(port, self.ip)
263 self.http_server.listen(port, self.ip)
256 except socket.error, e:
264 except socket.error, e:
257 if e.errno != errno.EADDRINUSE:
265 if e.errno != errno.EADDRINUSE:
258 raise
266 raise
259 self.log.info('The port %i is already in use, trying another random port.' % port)
267 self.log.info('The port %i is already in use, trying another random port.' % port)
260 else:
268 else:
261 self.port = port
269 self.port = port
262 break
270 break
263
271
264 def start(self):
272 def start(self):
265 ip = self.ip if self.ip else '[all ip addresses on your system]'
273 ip = self.ip if self.ip else '[all ip addresses on your system]'
266 proto = 'https' if self.certfile else 'http'
274 proto = 'https' if self.certfile else 'http'
267 self.log.info("The IPython Notebook is running at: %s://%s:%i" % (proto,
275 self.log.info("The IPython Notebook is running at: %s://%s:%i" % (proto,
268 ip,
276 ip,
269 self.port))
277 self.port))
270 ioloop.IOLoop.instance().start()
278 ioloop.IOLoop.instance().start()
271
279
272 #-----------------------------------------------------------------------------
280 #-----------------------------------------------------------------------------
273 # Main entry point
281 # Main entry point
274 #-----------------------------------------------------------------------------
282 #-----------------------------------------------------------------------------
275
283
276 def launch_new_instance():
284 def launch_new_instance():
277 app = IPythonNotebookApp()
285 app = IPythonNotebookApp()
278 app.initialize()
286 app.initialize()
279 app.start()
287 app.start()
280
288
@@ -1,160 +1,166
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Kernel
9 // Kernel
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var utils = IPython.utils;
14 var utils = IPython.utils;
15
15
16 var Kernel = function () {
16 var Kernel = function () {
17 this.kernel_id = null;
17 this.kernel_id = null;
18 this.base_url = "/kernels";
18 this.base_url = "/kernels";
19 this.kernel_url = null;
19 this.kernel_url = null;
20 this.shell_channel = null;
20 this.shell_channel = null;
21 this.iopub_channel = null;
21 this.iopub_channel = null;
22 this.running = false;
22 this.running = false;
23
23
24 this.username = "username";
24 this.username = "username";
25 this.session_id = utils.uuid();
25 this.session_id = utils.uuid();
26
26
27 if (typeof(WebSocket) !== 'undefined') {
27 if (typeof(WebSocket) !== 'undefined') {
28 this.WebSocket = WebSocket
28 this.WebSocket = WebSocket
29 } else if (typeof(MozWebSocket) !== 'undefined') {
29 } else if (typeof(MozWebSocket) !== 'undefined') {
30 this.WebSocket = MozWebSocket
30 this.WebSocket = MozWebSocket
31 } else {
31 } else {
32 alert('Your browser does not have WebSocket support, please try Chrome, Safari or Firefox 6. Firefox 4 and 5 are also supported by you have to enable WebSockets in about:config.');
32 alert('Your browser does not have WebSocket support, please try Chrome, Safari or Firefox 6. Firefox 4 and 5 are also supported by you have to enable WebSockets in about:config.');
33 };
33 };
34 };
34 };
35
35
36
36
37 Kernel.prototype.get_msg = function (msg_type, content) {
37 Kernel.prototype.get_msg = function (msg_type, content) {
38 var msg = {
38 var msg = {
39 header : {
39 header : {
40 msg_id : utils.uuid(),
40 msg_id : utils.uuid(),
41 username : this.username,
41 username : this.username,
42 session : this.session_id,
42 session : this.session_id,
43 msg_type : msg_type
43 msg_type : msg_type
44 },
44 },
45 content : content,
45 content : content,
46 parent_header : {}
46 parent_header : {}
47 };
47 };
48 return msg;
48 return msg;
49 }
49 }
50
50
51 Kernel.prototype.start = function (notebook_id, callback) {
51 Kernel.prototype.start = function (notebook_id, callback) {
52 var that = this;
52 var that = this;
53 if (!this.running) {
53 if (!this.running) {
54 var qs = $.param({notebook:notebook_id});
54 var qs = $.param({notebook:notebook_id});
55 $.post(this.base_url + '?' + qs,
55 $.post(this.base_url + '?' + qs,
56 function (kernel_id) {
56 function (kernel_id) {
57 that._handle_start_kernel(kernel_id, callback);
57 that._handle_start_kernel(kernel_id, callback);
58 },
58 },
59 'json'
59 'json'
60 );
60 );
61 };
61 };
62 };
62 };
63
63
64
64
65 Kernel.prototype.restart = function (callback) {
65 Kernel.prototype.restart = function (callback) {
66 IPython.kernel_status_widget.status_restarting();
66 IPython.kernel_status_widget.status_restarting();
67 var url = this.kernel_url + "/restart";
67 var url = this.kernel_url + "/restart";
68 var that = this;
68 var that = this;
69 if (this.running) {
69 if (this.running) {
70 this.stop_channels();
70 this.stop_channels();
71 $.post(url,
71 $.post(url,
72 function (kernel_id) {
72 function (kernel_id) {
73 that._handle_start_kernel(kernel_id, callback);
73 that._handle_start_kernel(kernel_id, callback);
74 },
74 },
75 'json'
75 'json'
76 );
76 );
77 };
77 };
78 };
78 };
79
79
80
80
81 Kernel.prototype._handle_start_kernel = function (json, callback) {
81 Kernel.prototype._handle_start_kernel = function (json, callback) {
82 this.running = true;
82 this.running = true;
83 this.kernel_id = json.kernel_id;
83 this.kernel_id = json.kernel_id;
84 this.ws_url = json.ws_url;
84 this.ws_url = json.ws_url;
85 this.kernel_url = this.base_url + "/" + this.kernel_id;
85 this.kernel_url = this.base_url + "/" + this.kernel_id;
86 this.start_channels();
86 this.start_channels();
87 callback();
87 callback();
88 IPython.kernel_status_widget.status_idle();
88 IPython.kernel_status_widget.status_idle();
89 };
89 };
90
90
91
91
92 Kernel.prototype.start_channels = function () {
92 Kernel.prototype.start_channels = function () {
93 this.stop_channels();
93 this.stop_channels();
94 var ws_url = this.ws_url + this.kernel_url;
94 var ws_url = this.ws_url + this.kernel_url;
95 console.log("Starting WS:", ws_url);
95 console.log("Starting WS:", ws_url);
96 this.shell_channel = new this.WebSocket(ws_url + "/shell");
96 this.shell_channel = new this.WebSocket(ws_url + "/shell");
97 this.iopub_channel = new this.WebSocket(ws_url + "/iopub");
97 this.iopub_channel = new this.WebSocket(ws_url + "/iopub");
98 send_cookie = function(){
99 this.send(document.cookie);
100 console.log(this);
101 }
102 this.shell_channel.onopen = send_cookie;
103 this.iopub_channel.onopen = send_cookie;
98 };
104 };
99
105
100
106
101 Kernel.prototype.stop_channels = function () {
107 Kernel.prototype.stop_channels = function () {
102 if (this.shell_channel !== null) {
108 if (this.shell_channel !== null) {
103 this.shell_channel.close();
109 this.shell_channel.close();
104 this.shell_channel = null;
110 this.shell_channel = null;
105 };
111 };
106 if (this.iopub_channel !== null) {
112 if (this.iopub_channel !== null) {
107 this.iopub_channel.close();
113 this.iopub_channel.close();
108 this.iopub_channel = null;
114 this.iopub_channel = null;
109 };
115 };
110 };
116 };
111
117
112 Kernel.prototype.execute = function (code) {
118 Kernel.prototype.execute = function (code) {
113 var content = {
119 var content = {
114 code : code,
120 code : code,
115 silent : false,
121 silent : false,
116 user_variables : [],
122 user_variables : [],
117 user_expressions : {}
123 user_expressions : {}
118 };
124 };
119 var msg = this.get_msg("execute_request", content);
125 var msg = this.get_msg("execute_request", content);
120 this.shell_channel.send(JSON.stringify(msg));
126 this.shell_channel.send(JSON.stringify(msg));
121 return msg.header.msg_id;
127 return msg.header.msg_id;
122 }
128 }
123
129
124
130
125 Kernel.prototype.complete = function (line, cursor_pos) {
131 Kernel.prototype.complete = function (line, cursor_pos) {
126 var content = {
132 var content = {
127 text : '',
133 text : '',
128 line : line,
134 line : line,
129 cursor_pos : cursor_pos
135 cursor_pos : cursor_pos
130 };
136 };
131 var msg = this.get_msg("complete_request", content);
137 var msg = this.get_msg("complete_request", content);
132 this.shell_channel.send(JSON.stringify(msg));
138 this.shell_channel.send(JSON.stringify(msg));
133 return msg.header.msg_id;
139 return msg.header.msg_id;
134 }
140 }
135
141
136
142
137 Kernel.prototype.interrupt = function () {
143 Kernel.prototype.interrupt = function () {
138 if (this.running) {
144 if (this.running) {
139 $.post(this.kernel_url + "/interrupt");
145 $.post(this.kernel_url + "/interrupt");
140 };
146 };
141 };
147 };
142
148
143
149
144 Kernel.prototype.kill = function () {
150 Kernel.prototype.kill = function () {
145 if (this.running) {
151 if (this.running) {
146 this.running = false;
152 this.running = false;
147 var settings = {
153 var settings = {
148 cache : false,
154 cache : false,
149 type : "DELETE",
155 type : "DELETE",
150 };
156 };
151 $.ajax(this.kernel_url, settings);
157 $.ajax(this.kernel_url, settings);
152 };
158 };
153 };
159 };
154
160
155 IPython.Kernel = Kernel;
161 IPython.Kernel = Kernel;
156
162
157 return IPython;
163 return IPython;
158
164
159 }(IPython));
165 }(IPython));
160
166
General Comments 0
You need to be logged in to leave comments. Login now