Show More
@@ -139,7 +139,16 b' class AuthenticatedHandler(web.RequestHandler):' | |||||
139 | return True |
|
139 | return True | |
140 | else: |
|
140 | else: | |
141 | return False |
|
141 | return False | |
|
142 | ||||
|
143 | @property | |||
|
144 | def ws_url(self): | |||
|
145 | """websocket url matching the current request | |||
142 |
|
|
146 | ||
|
147 | turns http[s]://host[:port] into | |||
|
148 | ws[s]://host[:port] | |||
|
149 | """ | |||
|
150 | proto = self.request.protocol.replace('http', 'ws') | |||
|
151 | return "%s://%s" % (proto, self.request.host) | |||
143 |
|
152 | |||
144 |
|
153 | |||
145 | class ProjectDashboardHandler(AuthenticatedHandler): |
|
154 | class ProjectDashboardHandler(AuthenticatedHandler): | |
@@ -221,8 +230,7 b' class MainKernelHandler(AuthenticatedHandler):' | |||||
221 | km = self.application.kernel_manager |
|
230 | km = self.application.kernel_manager | |
222 | notebook_id = self.get_argument('notebook', default=None) |
|
231 | notebook_id = self.get_argument('notebook', default=None) | |
223 | kernel_id = km.start_kernel(notebook_id) |
|
232 | kernel_id = km.start_kernel(notebook_id) | |
224 | ws_url = self.application.ipython_app.get_ws_url() |
|
233 | data = {'ws_url':self.ws_url,'kernel_id':kernel_id} | |
225 | data = {'ws_url':ws_url,'kernel_id':kernel_id} |
|
|||
226 | self.set_header('Location', '/'+kernel_id) |
|
234 | self.set_header('Location', '/'+kernel_id) | |
227 | self.finish(jsonapi.dumps(data)) |
|
235 | self.finish(jsonapi.dumps(data)) | |
228 |
|
236 | |||
@@ -249,8 +257,7 b' class KernelActionHandler(AuthenticatedHandler):' | |||||
249 | self.set_status(204) |
|
257 | self.set_status(204) | |
250 | if action == 'restart': |
|
258 | if action == 'restart': | |
251 | new_kernel_id = km.restart_kernel(kernel_id) |
|
259 | new_kernel_id = km.restart_kernel(kernel_id) | |
252 | ws_url = self.application.ipython_app.get_ws_url() |
|
260 | data = {'ws_url':self.ws_url,'kernel_id':new_kernel_id} | |
253 | data = {'ws_url':ws_url,'kernel_id':new_kernel_id} |
|
|||
254 | self.set_header('Location', '/'+new_kernel_id) |
|
261 | self.set_header('Location', '/'+new_kernel_id) | |
255 | self.write(jsonapi.dumps(data)) |
|
262 | self.write(jsonapi.dumps(data)) | |
256 | self.finish() |
|
263 | self.finish() |
@@ -147,7 +147,6 b' aliases.update({' | |||||
147 | 'port': 'NotebookApp.port', |
|
147 | 'port': 'NotebookApp.port', | |
148 | 'keyfile': 'NotebookApp.keyfile', |
|
148 | 'keyfile': 'NotebookApp.keyfile', | |
149 | 'certfile': 'NotebookApp.certfile', |
|
149 | 'certfile': 'NotebookApp.certfile', | |
150 | 'ws-hostname': 'NotebookApp.ws_hostname', |
|
|||
151 | 'notebook-dir': 'NotebookManager.notebook_dir', |
|
150 | 'notebook-dir': 'NotebookManager.notebook_dir', | |
152 | }) |
|
151 | }) | |
153 |
|
152 | |||
@@ -155,7 +154,7 b' aliases.update({' | |||||
155 | # multi-kernel evironment: |
|
154 | # multi-kernel evironment: | |
156 | aliases.pop('f', None) |
|
155 | aliases.pop('f', None) | |
157 |
|
156 | |||
158 |
notebook_aliases = [u'port', u'ip', u'keyfile', u'certfile', |
|
157 | notebook_aliases = [u'port', u'ip', u'keyfile', u'certfile', | |
159 | u'notebook-dir'] |
|
158 | u'notebook-dir'] | |
160 |
|
159 | |||
161 | #----------------------------------------------------------------------------- |
|
160 | #----------------------------------------------------------------------------- | |
@@ -200,13 +199,6 b' class NotebookApp(BaseIPythonApplication):' | |||||
200 | help="The port the notebook server will listen on." |
|
199 | help="The port the notebook server will listen on." | |
201 | ) |
|
200 | ) | |
202 |
|
201 | |||
203 | ws_hostname = Unicode(LOCALHOST, config=True, |
|
|||
204 | help="""The FQDN or IP for WebSocket connections. The default will work |
|
|||
205 | fine when the server is listening on localhost, but this needs to |
|
|||
206 | be set if the ip option is used. It will be used as the hostname part |
|
|||
207 | of the WebSocket url: ws://hostname/path.""" |
|
|||
208 | ) |
|
|||
209 |
|
||||
210 | certfile = Unicode(u'', config=True, |
|
202 | certfile = Unicode(u'', config=True, | |
211 | help="""The full path to an SSL/TLS certificate file.""" |
|
203 | help="""The full path to an SSL/TLS certificate file.""" | |
212 | ) |
|
204 | ) | |
@@ -226,14 +218,6 b' class NotebookApp(BaseIPythonApplication):' | |||||
226 | help="Whether to prevent editing/execution of notebooks." |
|
218 | help="Whether to prevent editing/execution of notebooks." | |
227 | ) |
|
219 | ) | |
228 |
|
220 | |||
229 | def get_ws_url(self): |
|
|||
230 | """Return the WebSocket URL for this server.""" |
|
|||
231 | if self.certfile: |
|
|||
232 | prefix = u'wss://' |
|
|||
233 | else: |
|
|||
234 | prefix = u'ws://' |
|
|||
235 | return prefix + self.ws_hostname + u':' + unicode(self.port) |
|
|||
236 |
|
||||
237 | def parse_command_line(self, argv=None): |
|
221 | def parse_command_line(self, argv=None): | |
238 | super(NotebookApp, self).parse_command_line(argv) |
|
222 | super(NotebookApp, self).parse_command_line(argv) | |
239 | if argv is None: |
|
223 | if argv is None: |
@@ -27,7 +27,7 b' var IPython = (function (IPython) {' | |||||
27 | } else if (typeof(MozWebSocket) !== 'undefined') { |
|
27 | } else if (typeof(MozWebSocket) !== 'undefined') { | |
28 | this.WebSocket = MozWebSocket |
|
28 | this.WebSocket = MozWebSocket | |
29 | } else { |
|
29 | } else { | |
30 | 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.'); |
|
30 | 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.'); | |
31 | }; |
|
31 | }; | |
32 | }; |
|
32 | }; | |
33 |
|
33 | |||
@@ -87,8 +87,37 b' var IPython = (function (IPython) {' | |||||
87 | IPython.kernel_status_widget.status_idle(); |
|
87 | IPython.kernel_status_widget.status_idle(); | |
88 | }; |
|
88 | }; | |
89 |
|
89 | |||
|
90 | Kernel.prototype._websocket_closed = function(ws_url, early){ | |||
|
91 | var msg; | |||
|
92 | var parent_item = $('body'); | |||
|
93 | if (early) { | |||
|
94 | msg = "Websocket connection to " + ws_url + " could not be established.<br/>" + | |||
|
95 | " You will NOT be able to run code.<br/>" + | |||
|
96 | " Your browser may not be compatible with the websocket version in the server," + | |||
|
97 | " or if the url does not look right, there could be an error in the" + | |||
|
98 | " server's configuration." | |||
|
99 | } else { | |||
|
100 | msg = "Websocket connection closed unexpectedly.<br/>" + | |||
|
101 | " The kernel will no longer be responsive." | |||
|
102 | } | |||
|
103 | var dialog = $('<div/>'); | |||
|
104 | dialog.html(msg); | |||
|
105 | parent_item.append(dialog); | |||
|
106 | dialog.dialog({ | |||
|
107 | resizable: false, | |||
|
108 | modal: true, | |||
|
109 | title: "Websocket closed", | |||
|
110 | buttons : { | |||
|
111 | "Okay": function () { | |||
|
112 | $(this).dialog('close'); | |||
|
113 | } | |||
|
114 | } | |||
|
115 | }); | |||
|
116 | ||||
|
117 | } | |||
90 |
|
118 | |||
91 | Kernel.prototype.start_channels = function () { |
|
119 | Kernel.prototype.start_channels = function () { | |
|
120 | var that = this; | |||
92 | this.stop_channels(); |
|
121 | this.stop_channels(); | |
93 | var ws_url = this.ws_url + this.kernel_url; |
|
122 | var ws_url = this.ws_url + this.kernel_url; | |
94 | console.log("Starting WS:", ws_url); |
|
123 | console.log("Starting WS:", ws_url); | |
@@ -97,17 +126,45 b' var IPython = (function (IPython) {' | |||||
97 | send_cookie = function(){ |
|
126 | send_cookie = function(){ | |
98 | this.send(document.cookie); |
|
127 | this.send(document.cookie); | |
99 | } |
|
128 | } | |
|
129 | var already_called_onclose = false; // only alert once | |||
|
130 | ws_closed_early = function(evt){ | |||
|
131 | if (already_called_onclose){ | |||
|
132 | return; | |||
|
133 | } | |||
|
134 | already_called_onclose = true; | |||
|
135 | if ( ! evt.wasClean ){ | |||
|
136 | that._websocket_closed(ws_url, true); | |||
|
137 | } | |||
|
138 | } | |||
|
139 | ws_closed_late = function(evt){ | |||
|
140 | if (already_called_onclose){ | |||
|
141 | return; | |||
|
142 | } | |||
|
143 | already_called_onclose = true; | |||
|
144 | if ( ! evt.wasClean ){ | |||
|
145 | that._websocket_closed(ws_url, false); | |||
|
146 | } | |||
|
147 | } | |||
100 | this.shell_channel.onopen = send_cookie; |
|
148 | this.shell_channel.onopen = send_cookie; | |
|
149 | this.shell_channel.onclose = ws_closed_early; | |||
101 | this.iopub_channel.onopen = send_cookie; |
|
150 | this.iopub_channel.onopen = send_cookie; | |
|
151 | this.iopub_channel.onclose = ws_closed_early; | |||
|
152 | // switch from early-close to late-close message after 1s | |||
|
153 | setTimeout(function(){ | |||
|
154 | that.shell_channel.onclose = ws_closed_late; | |||
|
155 | that.iopub_channel.onclose = ws_closed_late; | |||
|
156 | }, 1000); | |||
102 | }; |
|
157 | }; | |
103 |
|
158 | |||
104 |
|
159 | |||
105 | Kernel.prototype.stop_channels = function () { |
|
160 | Kernel.prototype.stop_channels = function () { | |
106 | if (this.shell_channel !== null) { |
|
161 | if (this.shell_channel !== null) { | |
|
162 | this.shell_channel.onclose = function (evt) {null}; | |||
107 | this.shell_channel.close(); |
|
163 | this.shell_channel.close(); | |
108 | this.shell_channel = null; |
|
164 | this.shell_channel = null; | |
109 | }; |
|
165 | }; | |
110 | if (this.iopub_channel !== null) { |
|
166 | if (this.iopub_channel !== null) { | |
|
167 | this.iopub_channel.onclose = function (evt) {null}; | |||
111 | this.iopub_channel.close(); |
|
168 | this.iopub_channel.close(); | |
112 | this.iopub_channel = null; |
|
169 | this.iopub_channel = null; | |
113 | }; |
|
170 | }; |
General Comments 0
You need to be logged in to leave comments.
Login now