Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configure absolute and server URL prefixes #85

Merged
merged 10 commits into from
Jan 30, 2019
26 changes: 26 additions & 0 deletions docs/server-process.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,32 @@ pairs.
* A callable that takes any :ref:`callable arguments <server-process/callable-argument>`,
and returns a dictionary of strings that are used & treated same as above.

#. **absolute_url**

*True* if the URL as seen by the proxied application should be the full URL
sent by the user. *False* if the URL as seen by the proxied application should
see the URL after the parts specific to jupyter-server-proxy have been stripped.

For example, with the following config:

.. code:: python

c.ServerProxy.servers = {
'test-server': {
'command': ['python3', '-m', 'http.server', '{port}'],
'absolute_url': False
}
}

When a user requests ``/test-server/some-url``, the proxied server will see it
as a request for ``/some-url`` - the ``/test-server`` part is stripped out.

If ``absolute_url`` is set to ``True`` instead, the proxied server will see it
as a request for ``/test-server/some-url`` instead - without any stripping.

This is very useful with applications that require a ``base_url`` to be set.


#. **launcher_entry**

A dictionary with options on if / how an entry in the classic Jupyter Notebook
Expand Down
16 changes: 8 additions & 8 deletions jupyter_server_proxy/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from collections import namedtuple
from .utils import call_with_asked_args

def _make_serverproxy_handler(name, command, environment, timeout, rewrite):
def _make_serverproxy_handler(name, command, environment, timeout, absolute_url):
"""
Create a SuperviseAndProxyHandler subclass with given parameters
"""
Expand All @@ -19,7 +19,7 @@ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.name = name
self.proxy_base = name
self.rewrite = rewrite
self.absolute_url = absolute_url

@property
def process_args(self):
Expand Down Expand Up @@ -79,7 +79,7 @@ def make_handlers(base_url, server_processes):
sp.command,
sp.environment,
sp.timeout,
sp.rewrite,
sp.absolute_url,
)
handlers.append((
ujoin(base_url, sp.name, r'(.*)'), handler, dict(state={}),
Expand All @@ -91,7 +91,7 @@ def make_handlers(base_url, server_processes):

LauncherEntry = namedtuple('LauncherEntry', ['enabled', 'icon_path', 'title'])
ServerProcess = namedtuple('ServerProcess', [
'name', 'command', 'environment', 'timeout', 'rewrite', 'launcher_entry'])
'name', 'command', 'environment', 'timeout', 'absolute_url', 'launcher_entry'])

def make_server_process(name, server_process_config):
le = server_process_config.get('launcher_entry', {})
Expand All @@ -100,7 +100,7 @@ def make_server_process(name, server_process_config):
command=server_process_config['command'],
environment=server_process_config.get('environment', {}),
timeout=server_process_config.get('timeout', 5),
rewrite=server_process_config.get('rewrite', '/'),
absolute_url=server_process_config.get('absolute_url', False),
launcher_entry=LauncherEntry(
enabled=le.get('enabled', True),
icon_path=le.get('icon_path'),
Expand Down Expand Up @@ -134,9 +134,9 @@ class ServerProxy(Configurable):
timeout
Timeout in seconds for the process to become ready, default 5s.

rewrite
Proxy requests default to being rewritten to '/'. If this is ''
(empty) the absolute URL will be sent to the backend instead.
absolute_url
Proxy requests default to being rewritten to '/'. If this is True,
the absolute URL will be sent to the backend instead.

launcher_entry
A dictionary of various options for entries in classic notebook / jupyterlab launchers.
Expand Down
41 changes: 20 additions & 21 deletions jupyter_server_proxy/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class LocalProxyHandler(WebSocketHandlerMixin, IPythonHandler):

def __init__(self, *args, **kwargs):
self.proxy_base = ''
self.rewrite = kwargs.pop('rewrite', '/')
self.absolute_url = kwargs.pop('absolute_url', False)
super().__init__(*args, **kwargs)

async def open(self, port, proxied_path=''):
Expand All @@ -45,13 +45,7 @@ async def open(self, port, proxied_path=''):
if not proxied_path.startswith('/'):
proxied_path = '/' + proxied_path

client_uri = '{uri}:{port}{path}'.format(
uri='ws://127.0.0.1',
port=port,
path=proxied_path
)
if self.request.query:
client_uri += '?' + self.request.query
client_uri = self.get_client_uri('ws', port, proxied_path)
headers = self.request.headers

def message_cb(message):
Expand Down Expand Up @@ -142,32 +136,37 @@ def _get_context_path(self, port):
"""
if self.proxy_base:
return url_path_join(self.base_url, self.proxy_base)
if self.rewrite == '/':
return url_path_join(self.base_url, 'proxy', str(port))
if self.rewrite == '':
if self.absolute_url:
return url_path_join(self.base_url, 'proxy', 'absolute', str(port))
raise ValueError('Unsupported rewrite: "{}"'.format(self.rewrite))
else:
return url_path_join(self.base_url, 'proxy', str(port))

def _build_proxy_request(self, port, proxied_path, body):
def get_client_uri(self, protocol, port, proxied_path):
context_path = self._get_context_path(port)
if self.rewrite:
client_path = proxied_path
else:
if self.absolute_url:
client_path = url_path_join(context_path, proxied_path)
else:
client_path = proxied_path

client_uri = '{uri}:{port}{path}'.format(
uri='http://localhost',
client_uri = '{protocol}://{host}:{port}{path}'.format(
protocol=protocol,
host='localhost',
port=port,
path=client_path
)
if self.request.query:
client_uri += '?' + self.request.query

return client_uri

def _build_proxy_request(self, port, proxied_path, body):

headers = self.proxy_request_headers()

client_uri = self.get_client_uri('http', port, proxied_path)
# Some applications check X-Forwarded-Context and X-ProxyContextPath
# headers to see if and where they are being proxied from.
if self.rewrite == '/':
if not self.absolute_url:
yuvipanda marked this conversation as resolved.
Show resolved Hide resolved
headers['X-Forwarded-Context'] = context_path
headers['X-ProxyContextPath'] = context_path

Expand Down Expand Up @@ -412,9 +411,9 @@ def setup_handlers(web_app):
host_pattern = '.*$'
web_app.add_handlers('.*', [
(url_path_join(web_app.settings['base_url'], r'/proxy/(\d+)(.*)'),
LocalProxyHandler, {'rewrite': '/'}),
LocalProxyHandler, {'absolute_url': False}),
(url_path_join(web_app.settings['base_url'], r'/proxy/absolute/(\d+)(.*)'),
LocalProxyHandler, {'rewrite': ''}),
LocalProxyHandler, {'absolute_url': True}),
])

# vim: set et ts=4 sw=4: