##// END OF EJS Templates
fix: display secure url with proper protocol
Satrajit Ghosh -
Show More
@@ -1,277 +1,282 b''
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 (
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"/new", NewHandler),
83 (r"/new", NewHandler),
84 (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
84 (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
85 (r"/kernels", MainKernelHandler),
85 (r"/kernels", MainKernelHandler),
86 (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
86 (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
87 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
87 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
88 (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
88 (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
89 (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
89 (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
90 (r"/notebooks", NotebookRootHandler),
90 (r"/notebooks", NotebookRootHandler),
91 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
91 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
92 (r"/rstservice/render", RSTHandler)
92 (r"/rstservice/render", RSTHandler)
93 ]
93 ]
94 settings = dict(
94 settings = dict(
95 template_path=os.path.join(os.path.dirname(__file__), "templates"),
95 template_path=os.path.join(os.path.dirname(__file__), "templates"),
96 static_path=os.path.join(os.path.dirname(__file__), "static"),
96 static_path=os.path.join(os.path.dirname(__file__), "static"),
97 )
97 )
98 web.Application.__init__(self, handlers, **settings)
98 web.Application.__init__(self, handlers, **settings)
99
99
100 self.kernel_manager = kernel_manager
100 self.kernel_manager = kernel_manager
101 self.log = log
101 self.log = log
102 self.notebook_manager = notebook_manager
102 self.notebook_manager = notebook_manager
103 self.ipython_app = ipython_app
103 self.ipython_app = ipython_app
104
104
105
105
106 #-----------------------------------------------------------------------------
106 #-----------------------------------------------------------------------------
107 # Aliases and Flags
107 # Aliases and Flags
108 #-----------------------------------------------------------------------------
108 #-----------------------------------------------------------------------------
109
109
110 flags = dict(ipkernel_flags)
110 flags = dict(ipkernel_flags)
111
111
112 # the flags that are specific to the frontend
112 # the flags that are specific to the frontend
113 # these must be scrubbed before being passed to the kernel,
113 # these must be scrubbed before being passed to the kernel,
114 # or it will raise an error on unrecognized flags
114 # or it will raise an error on unrecognized flags
115 notebook_flags = []
115 notebook_flags = []
116
116
117 aliases = dict(ipkernel_aliases)
117 aliases = dict(ipkernel_aliases)
118
118
119 aliases.update({
119 aliases.update({
120 'ip': 'IPythonNotebookApp.ip',
120 'ip': 'IPythonNotebookApp.ip',
121 'port': 'IPythonNotebookApp.port',
121 'port': 'IPythonNotebookApp.port',
122 'keyfile': 'IPythonNotebookApp.keyfile',
122 'keyfile': 'IPythonNotebookApp.keyfile',
123 'certfile': 'IPythonNotebookApp.certfile',
123 'certfile': 'IPythonNotebookApp.certfile',
124 'ws-hostname': 'IPythonNotebookApp.ws_hostname',
124 'ws-hostname': 'IPythonNotebookApp.ws_hostname',
125 'notebook-dir': 'NotebookManager.notebook_dir'
125 'notebook-dir': 'NotebookManager.notebook_dir'
126 })
126 })
127
127
128 notebook_aliases = [u'port', u'ip', u'keyfile', u'certfile', u'ws-hostname',
128 notebook_aliases = [u'port', u'ip', u'keyfile', u'certfile', u'ws-hostname',
129 u'notebook-dir']
129 u'notebook-dir']
130
130
131 #-----------------------------------------------------------------------------
131 #-----------------------------------------------------------------------------
132 # IPythonNotebookApp
132 # IPythonNotebookApp
133 #-----------------------------------------------------------------------------
133 #-----------------------------------------------------------------------------
134
134
135 class IPythonNotebookApp(BaseIPythonApplication):
135 class IPythonNotebookApp(BaseIPythonApplication):
136
136
137 name = 'ipython-notebook'
137 name = 'ipython-notebook'
138 default_config_file_name='ipython_notebook_config.py'
138 default_config_file_name='ipython_notebook_config.py'
139
139
140 description = """
140 description = """
141 The IPython HTML Notebook.
141 The IPython HTML Notebook.
142
142
143 This launches a Tornado based HTML Notebook Server that serves up an
143 This launches a Tornado based HTML Notebook Server that serves up an
144 HTML5/Javascript Notebook client.
144 HTML5/Javascript Notebook client.
145 """
145 """
146 examples = _examples
146 examples = _examples
147
147
148 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
148 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
149 MappingKernelManager, NotebookManager]
149 MappingKernelManager, NotebookManager]
150 flags = Dict(flags)
150 flags = Dict(flags)
151 aliases = Dict(aliases)
151 aliases = Dict(aliases)
152
152
153 kernel_argv = List(Unicode)
153 kernel_argv = List(Unicode)
154
154
155 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
155 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
156 default_value=logging.INFO,
156 default_value=logging.INFO,
157 config=True,
157 config=True,
158 help="Set the log level by value or name.")
158 help="Set the log level by value or name.")
159
159
160 # Network related information.
160 # Network related information.
161
161
162 ip = Unicode(LOCALHOST, config=True,
162 ip = Unicode(LOCALHOST, config=True,
163 help="The IP address the notebook server will listen on."
163 help="The IP address the notebook server will listen on."
164 )
164 )
165
165
166 def _ip_changed(self, name, old, new):
166 def _ip_changed(self, name, old, new):
167 if new == u'*': self.ip = u''
167 if new == u'*': self.ip = u''
168
168
169 port = Int(8888, config=True,
169 port = Int(8888, config=True,
170 help="The port the notebook server will listen on."
170 help="The port the notebook server will listen on."
171 )
171 )
172
172
173 ws_hostname = Unicode(LOCALHOST, config=True,
173 ws_hostname = Unicode(LOCALHOST, config=True,
174 help="""The FQDN or IP for WebSocket connections. The default will work
174 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
175 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
176 be set if the ip option is used. It will be used as the hostname part
177 of the WebSocket url: ws://hostname/path."""
177 of the WebSocket url: ws://hostname/path."""
178 )
178 )
179
179
180 certfile = Unicode(u'', config=True,
180 certfile = Unicode(u'', config=True,
181 help="""The full path to an SSL/TLS certificate file."""
181 help="""The full path to an SSL/TLS certificate file."""
182 )
182 )
183
183
184 keyfile = Unicode(u'', config=True,
184 keyfile = Unicode(u'', config=True,
185 help="""The full path to a private key file for usage with SSL/TLS."""
185 help="""The full path to a private key file for usage with SSL/TLS."""
186 )
186 )
187
187
188 def get_ws_url(self):
188 def get_ws_url(self):
189 """Return the WebSocket URL for this server."""
189 """Return the WebSocket URL for this server."""
190 if self.certfile:
190 if self.certfile:
191 prefix = u'wss://'
191 prefix = u'wss://'
192 else:
192 else:
193 prefix = u'ws://'
193 prefix = u'ws://'
194 return prefix + self.ws_hostname + u':' + unicode(self.port)
194 return prefix + self.ws_hostname + u':' + unicode(self.port)
195
195
196 def parse_command_line(self, argv=None):
196 def parse_command_line(self, argv=None):
197 super(IPythonNotebookApp, self).parse_command_line(argv)
197 super(IPythonNotebookApp, self).parse_command_line(argv)
198 if argv is None:
198 if argv is None:
199 argv = sys.argv[1:]
199 argv = sys.argv[1:]
200
200
201 self.kernel_argv = list(argv) # copy
201 self.kernel_argv = list(argv) # copy
202 # Kernel should inherit default config file from frontend
202 # Kernel should inherit default config file from frontend
203 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
203 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
204 # Scrub frontend-specific flags
204 # Scrub frontend-specific flags
205 for a in argv:
205 for a in argv:
206 if a.startswith('-') and a.lstrip('-') in notebook_flags:
206 if a.startswith('-') and a.lstrip('-') in notebook_flags:
207 self.kernel_argv.remove(a)
207 self.kernel_argv.remove(a)
208 for a in argv:
208 for a in argv:
209 if a.startswith('-'):
209 if a.startswith('-'):
210 alias = a.lstrip('-').split('=')[0]
210 alias = a.lstrip('-').split('=')[0]
211 if alias in notebook_aliases:
211 if alias in notebook_aliases:
212 self.kernel_argv.remove(a)
212 self.kernel_argv.remove(a)
213
213
214 def init_configurables(self):
214 def init_configurables(self):
215 # Don't let Qt or ZMQ swallow KeyboardInterupts.
215 # Don't let Qt or ZMQ swallow KeyboardInterupts.
216 signal.signal(signal.SIGINT, signal.SIG_DFL)
216 signal.signal(signal.SIGINT, signal.SIG_DFL)
217
217
218 # Create a KernelManager and start a kernel.
218 # Create a KernelManager and start a kernel.
219 self.kernel_manager = MappingKernelManager(
219 self.kernel_manager = MappingKernelManager(
220 config=self.config, log=self.log, kernel_argv=self.kernel_argv
220 config=self.config, log=self.log, kernel_argv=self.kernel_argv
221 )
221 )
222 self.notebook_manager = NotebookManager(config=self.config, log=self.log)
222 self.notebook_manager = NotebookManager(config=self.config, log=self.log)
223 self.notebook_manager.list_notebooks()
223 self.notebook_manager.list_notebooks()
224
224
225 def init_logging(self):
225 def init_logging(self):
226 super(IPythonNotebookApp, self).init_logging()
226 super(IPythonNotebookApp, self).init_logging()
227 # This prevents double log messages because tornado use a root logger that
227 # 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
228 # 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.
229 # and all of its ancenstors until propagate is set to False.
230 self.log.propagate = False
230 self.log.propagate = False
231
231
232 def initialize(self, argv=None):
232 def initialize(self, argv=None):
233 super(IPythonNotebookApp, self).initialize(argv)
233 super(IPythonNotebookApp, self).initialize(argv)
234 self.init_configurables()
234 self.init_configurables()
235 self.web_app = NotebookWebApplication(
235 self.web_app = NotebookWebApplication(
236 self, self.kernel_manager, self.notebook_manager, self.log
236 self, self.kernel_manager, self.notebook_manager, self.log
237 )
237 )
238 if self.certfile:
238 if self.certfile:
239 ssl_options = dict(certfile=self.certfile)
239 ssl_options = dict(certfile=self.certfile)
240 if self.keyfile:
240 if self.keyfile:
241 ssl_options['keyfile'] = self.keyfile
241 ssl_options['keyfile'] = self.keyfile
242 else:
242 else:
243 ssl_options = None
243 ssl_options = None
244 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options)
244 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options)
245 if ssl_options is None and not self.ip:
245 if ssl_options is None and not self.ip:
246 self.log.critical('WARNING: the notebook server is listening on all IP addresses '
246 self.log.critical('WARNING: the notebook server is listening on all IP addresses '
247 'but not using any encryption or authentication. This is highly '
247 'but not using any encryption or authentication. This is highly '
248 'insecure and not recommended.')
248 'insecure and not recommended.')
249
249
250 # Try random ports centered around the default.
250 # Try random ports centered around the default.
251 from random import randint
251 from random import randint
252 n = 50 # Max number of attempts, keep reasonably large.
252 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)]:
253 for port in [self.port] + [self.port + randint(-2*n, 2*n) for i in range(n)]:
254 try:
254 try:
255 self.http_server.listen(port, self.ip)
255 self.http_server.listen(port, self.ip)
256 except socket.error, e:
256 except socket.error, e:
257 if e.errno != errno.EADDRINUSE:
257 if e.errno != errno.EADDRINUSE:
258 raise
258 raise
259 self.log.info('The port %i is already in use, trying another random port.' % port)
259 self.log.info('The port %i is already in use, trying another random port.' % port)
260 else:
260 else:
261 self.port = port
261 self.port = port
262 break
262 break
263
263
264 def start(self):
264 def start(self):
265 ip = self.ip if self.ip else '[all ip addresses on your system]'
265 ip = self.ip if self.ip else '[all ip addresses on your system]'
266 self.log.info("The IPython Notebook is running at: http://%s:%i" % (ip, self.port))
266 proto = 'http'
267 if self.certfile:
268 proto = 'https'
269 self.log.info("The IPython Notebook is running at: %s://%s:%i" % (proto,
270 ip,
271 self.port))
267 ioloop.IOLoop.instance().start()
272 ioloop.IOLoop.instance().start()
268
273
269 #-----------------------------------------------------------------------------
274 #-----------------------------------------------------------------------------
270 # Main entry point
275 # Main entry point
271 #-----------------------------------------------------------------------------
276 #-----------------------------------------------------------------------------
272
277
273 def launch_new_instance():
278 def launch_new_instance():
274 app = IPythonNotebookApp()
279 app = IPythonNotebookApp()
275 app.initialize()
280 app.initialize()
276 app.start()
281 app.start()
277
282
General Comments 0
You need to be logged in to leave comments. Login now