From 69c627cad87d62d0ac2608edb76f0c67c7a68377 2012-01-29 22:09:25 From: Fernando Perez Date: 2012-01-29 22:09:25 Subject: [PATCH] Merge pull request #1332 from astraw/alternate-url-prefix notebook - allow prefixes in URL path. This closes #1329. The basic idea is to allow prefixes in the path served. (E.g. serve on http://localhost:8888/ipython/ instead of http://localhost:8888/ ) This is useful for running the ipython notebook behind a proxy serving other content at other URLs. In that case, one would also need to proxy the websockets. Also, clean up some static paths in our html/css files by using template function calls instead of hardcoded paths. --- diff --git a/IPython/frontend/html/notebook/handlers.py b/IPython/frontend/html/notebook/handlers.py index 304f05c..05a06ac 100644 --- a/IPython/frontend/html/notebook/handlers.py +++ b/IPython/frontend/html/notebook/handlers.py @@ -168,12 +168,15 @@ class AuthenticatedHandler(RequestHandler): @property def ws_url(self): """websocket url matching the current request - + turns http[s]://host[:port] into ws[s]://host[:port] """ proto = self.request.protocol.replace('http', 'ws') - return "%s://%s" % (proto, self.request.host) + host = self.application.ipython_app.websocket_host # default to config value + if host == '': + host = self.request.host # get from request + return "%s://%s" % (proto, host) class AuthenticatedFileHandler(AuthenticatedHandler, web.StaticFileHandler): @@ -192,7 +195,8 @@ class ProjectDashboardHandler(AuthenticatedHandler): project = nbm.notebook_dir self.render( 'projectdashboard.html', project=project, - base_project_url=u'/', base_kernel_url=u'/', + base_project_url=self.application.ipython_app.base_project_url, + base_kernel_url=self.application.ipython_app.base_kernel_url, read_only=self.read_only, logged_in=self.logged_in, login_available=self.login_available @@ -255,7 +259,8 @@ class NewHandler(AuthenticatedHandler): self.render( 'notebook.html', project=project, notebook_id=notebook_id, - base_project_url=u'/', base_kernel_url=u'/', + base_project_url=self.application.ipython_app.base_project_url, + base_kernel_url=self.application.ipython_app.base_kernel_url, kill_kernel=False, read_only=False, logged_in=self.logged_in, @@ -276,7 +281,8 @@ class NamedNotebookHandler(AuthenticatedHandler): self.render( 'notebook.html', project=project, notebook_id=notebook_id, - base_project_url=u'/', base_kernel_url=u'/', + base_project_url=self.application.ipython_app.base_project_url, + base_kernel_url=self.application.ipython_app.base_kernel_url, kill_kernel=False, read_only=self.read_only, logged_in=self.logged_in, @@ -297,7 +303,8 @@ class PrintNotebookHandler(AuthenticatedHandler): self.render( 'printnotebook.html', project=project, notebook_id=notebook_id, - base_project_url=u'/', base_kernel_url=u'/', + base_project_url=self.application.ipython_app.base_project_url, + base_kernel_url=self.application.ipython_app.base_kernel_url, kill_kernel=False, read_only=self.read_only, logged_in=self.logged_in, @@ -637,7 +644,8 @@ class NotebookCopyHandler(AuthenticatedHandler): self.render( 'notebook.html', project=project, notebook_id=notebook_id, - base_project_url=u'/', base_kernel_url=u'/', + base_project_url=self.application.ipython_app.base_project_url, + base_kernel_url=self.application.ipython_app.base_kernel_url, kill_kernel=False, read_only=False, logged_in=self.logged_in, diff --git a/IPython/frontend/html/notebook/notebookapp.py b/IPython/frontend/html/notebook/notebookapp.py index 21252d8..4adf60e 100644 --- a/IPython/frontend/html/notebook/notebookapp.py +++ b/IPython/frontend/html/notebook/notebookapp.py @@ -85,12 +85,23 @@ ipython notebook --port=5555 --ip=* # Listen on port 5555, all interfaces """ #----------------------------------------------------------------------------- +# Helper functions +#----------------------------------------------------------------------------- + +def url_path_join(a,b): + if a.endswith('/') and b.startswith('/'): + return a[:-1]+b + else: + return a+b + +#----------------------------------------------------------------------------- # The Tornado web application #----------------------------------------------------------------------------- class NotebookWebApplication(web.Application): - def __init__(self, ipython_app, kernel_manager, notebook_manager, log, settings_overrides): + def __init__(self, ipython_app, kernel_manager, notebook_manager, log, + base_project_url, settings_overrides): handlers = [ (r"/", ProjectDashboardHandler), (r"/login", LoginHandler), @@ -119,7 +130,14 @@ class NotebookWebApplication(web.Application): # allow custom overrides for the tornado web app. settings.update(settings_overrides) - super(NotebookWebApplication, self).__init__(handlers, **settings) + # prepend base_project_url onto the patterns that we match + new_handlers = [] + for handler in handlers: + pattern = url_path_join(base_project_url, handler[0]) + new_handler = tuple([pattern]+list(handler[1:])) + new_handlers.append( new_handler ) + + super(NotebookWebApplication, self).__init__(new_handlers, **settings) self.kernel_manager = kernel_manager self.log = log @@ -277,7 +295,15 @@ class NotebookApp(BaseIPythonApplication): """set mathjax url to empty if mathjax is disabled""" if not new: self.mathjax_url = u'' - + + base_project_url = Unicode('/', config=True, + help='''The base URL for the notebook server''') + base_kernel_url = Unicode('/', config=True, + help='''The base URL for the kernel server''') + websocket_host = Unicode("", config=True, + help="""The hostname for the websocket server.""" + ) + mathjax_url = Unicode("", config=True, help="""The url for MathJax.js.""" ) @@ -285,9 +311,11 @@ class NotebookApp(BaseIPythonApplication): if not self.enable_mathjax: return u'' static_path = self.webapp_settings.get("static_path", os.path.join(os.path.dirname(__file__), "static")) + static_url_prefix = self.webapp_settings.get("static_url_prefix", + "/static/") if os.path.exists(os.path.join(static_path, 'mathjax', "MathJax.js")): self.log.info("Using local MathJax") - return u"/static/mathjax/MathJax.js" + return static_url_prefix+u"mathjax/MathJax.js" else: self.log.info("Using MathJax from CDN") return u"http://cdn.mathjax.org/mathjax/latest/MathJax.js" @@ -331,7 +359,7 @@ class NotebookApp(BaseIPythonApplication): """initialize tornado webapp and httpserver""" self.web_app = NotebookWebApplication( self, self.kernel_manager, self.notebook_manager, self.log, - self.webapp_settings + self.base_project_url, self.webapp_settings ) if self.certfile: ssl_options = dict(certfile=self.certfile) diff --git a/IPython/frontend/html/notebook/static/js/savewidget.js b/IPython/frontend/html/notebook/static/js/savewidget.js index e93493f..ad1f5f3 100644 --- a/IPython/frontend/html/notebook/static/js/savewidget.js +++ b/IPython/frontend/html/notebook/static/js/savewidget.js @@ -124,7 +124,7 @@ var IPython = (function (IPython) { SaveWidget.prototype.update_url = function () { var notebook_id = this.get_notebook_id(); if (notebook_id !== '') { - var new_url = '/'+notebook_id; + var new_url = $('body').data('baseProjectUrl') + notebook_id; window.history.replaceState({}, '', new_url); }; }; diff --git a/IPython/frontend/html/notebook/templates/layout.html b/IPython/frontend/html/notebook/templates/layout.html index 735c110..700a706 100644 --- a/IPython/frontend/html/notebook/templates/layout.html +++ b/IPython/frontend/html/notebook/templates/layout.html @@ -6,10 +6,10 @@ {% block title %}IPython Notebook{% end %} - - - - + + + + {% block stylesheet %} {% end %} @@ -21,7 +21,7 @@ - - - - - + + + + + {% block script %} {% end %} diff --git a/IPython/frontend/html/notebook/templates/notebook.html b/IPython/frontend/html/notebook/templates/notebook.html index 7280062..7e547f1 100644 --- a/IPython/frontend/html/notebook/templates/notebook.html +++ b/IPython/frontend/html/notebook/templates/notebook.html @@ -15,17 +15,17 @@ window.mathjax_url = "{{mathjax_url}}"; - - - + + + - + - - - - - + + + + + {% comment In the notebook, the read-only flag is used to determine %} {% comment whether to hide the side panels and switch off input %} @@ -39,7 +39,7 @@ > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/IPython/frontend/html/notebook/templates/printnotebook.html b/IPython/frontend/html/notebook/templates/printnotebook.html index 33937a9..003fe33 100644 --- a/IPython/frontend/html/notebook/templates/printnotebook.html +++ b/IPython/frontend/html/notebook/templates/printnotebook.html @@ -15,21 +15,21 @@ window.mathjax_url = "{{mathjax_url}}"; - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + {% comment In the notebook, the read-only flag is used to determine %} {% comment whether to hide the side panels and switch off input %} @@ -43,7 +43,7 @@ > - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/IPython/frontend/html/notebook/templates/projectdashboard.html b/IPython/frontend/html/notebook/templates/projectdashboard.html index 3dfc52b..9a64a0c 100644 --- a/IPython/frontend/html/notebook/templates/projectdashboard.html +++ b/IPython/frontend/html/notebook/templates/projectdashboard.html @@ -5,7 +5,7 @@ IPython Dashboard {% end %} {% block stylesheet %} - + {% end %} {% block meta %} @@ -38,6 +38,6 @@ data-base-kernel-url={{base_kernel_url}} {% end %} {% block script %} - - + + {% end %} diff --git a/docs/source/interactive/htmlnotebook.txt b/docs/source/interactive/htmlnotebook.txt index a829419..8575af4 100644 --- a/docs/source/interactive/htmlnotebook.txt +++ b/docs/source/interactive/htmlnotebook.txt @@ -355,7 +355,22 @@ uncomment and edit is here:: c.NotebookApp.port = 9999 You can then start the notebook and access it later by pointing your browser to -``https://your.host.com:9999``. +``https://your.host.com:9999`` with ``ipython notebook --profile=nbserver``. + +Running with a different URL prefix +=================================== + +The notebook dashboard (i.e. the default landing page with an overview +of all your notebooks) typically lives at a URL path of +"http://localhost:8888/". If you want to have it, and the rest of the +notebook, live under a sub-directory, +e.g. "http://localhost:8888/ipython/", you can do so with command-line +options like these: + + $ ipython notebook --NotebookApp.webapp_settings="\ + {'base_project_url':'/ipython/', \ + 'base_kernel_url':'/ipython/', \ + 'static_url_prefix':'/ipython/static/'}" .. _notebook_format: