Skip to content

Commit d02e213

Browse files
Merge branch 'master' into allow-remote-proxy
2 parents 35dd072 + 4b5ec52 commit d02e213

File tree

7 files changed

+80
-6
lines changed

7 files changed

+80
-6
lines changed

docs/server-process.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@ pairs.
9090
automatically select an unused port.
9191

9292

93+
#. **mappath**
94+
95+
Map request paths to proxied paths.
96+
Either a dictionary of request paths to proxied paths,
97+
or a callable that takes parameter ``path`` and returns the proxied path.
98+
99+
93100
#. **launcher_entry**
94101

95102
A dictionary with options on if / how an entry in the classic Jupyter Notebook

jupyter_server_proxy/config.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
except ImportError:
1616
from .utils import Callable
1717

18-
def _make_serverproxy_handler(name, command, environment, timeout, absolute_url, port):
18+
def _make_serverproxy_handler(name, command, environment, timeout, absolute_url, port, mappath):
1919
"""
2020
Create a SuperviseAndProxyHandler subclass with given parameters
2121
"""
@@ -27,6 +27,7 @@ def __init__(self, *args, **kwargs):
2727
self.proxy_base = name
2828
self.absolute_url = absolute_url
2929
self.requested_port = port
30+
self.mappath = mappath
3031

3132
@property
3233
def process_args(self):
@@ -88,6 +89,7 @@ def make_handlers(base_url, server_processes):
8889
sp.timeout,
8990
sp.absolute_url,
9091
sp.port,
92+
sp.mappath,
9193
)
9294
handlers.append((
9395
ujoin(base_url, sp.name, r'(.*)'), handler, dict(state={}),
@@ -99,7 +101,7 @@ def make_handlers(base_url, server_processes):
99101

100102
LauncherEntry = namedtuple('LauncherEntry', ['enabled', 'icon_path', 'title'])
101103
ServerProcess = namedtuple('ServerProcess', [
102-
'name', 'command', 'environment', 'timeout', 'absolute_url', 'port', 'launcher_entry'])
104+
'name', 'command', 'environment', 'timeout', 'absolute_url', 'port', 'mappath', 'launcher_entry'])
103105

104106
def make_server_process(name, server_process_config):
105107
le = server_process_config.get('launcher_entry', {})
@@ -110,6 +112,7 @@ def make_server_process(name, server_process_config):
110112
timeout=server_process_config.get('timeout', 5),
111113
absolute_url=server_process_config.get('absolute_url', False),
112114
port=server_process_config.get('port', 0),
115+
mappath=server_process_config.get('mappath', {}),
113116
launcher_entry=LauncherEntry(
114117
enabled=le.get('enabled', True),
115118
icon_path=le.get('icon_path'),
@@ -150,6 +153,11 @@ class ServerProxy(Configurable):
150153
port
151154
Set the port that the service will listen on. The default is to automatically select an unused port.
152155
156+
mappath
157+
Map request paths to proxied paths.
158+
Either a dictionary of request paths to proxied paths,
159+
or a callable that takes parameter ``path`` and returns the proxied path.
160+
153161
launcher_entry
154162
A dictionary of various options for entries in classic notebook / jupyterlab launchers.
155163

jupyter_server_proxy/handlers.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from notebook.utils import url_path_join
1717
from notebook.base.handlers import IPythonHandler, utcnow
1818

19+
from .utils import call_with_asked_args
1920
from .websocket import WebSocketHandlerMixin, pingable_ws_connect
2021
from simpervisor import SupervisedProcess
2122

@@ -45,7 +46,7 @@ def __init__(self, *args, **kwargs):
4546
self.host_whitelist = kwargs.pop('host_whitelist', ['localhost', '127.0.0.1'])
4647
super().__init__(*args, **kwargs)
4748

48-
# Support all the methods that torando does by default except for GET which
49+
# Support all the methods that tornado does by default except for GET which
4950
# is passed to WebSocketHandlerMixin and then to WebSocketHandler.
5051

5152
async def open(self, port, proxied_path):
@@ -395,6 +396,7 @@ class SuperviseAndProxyHandler(LocalProxyHandler):
395396

396397
def __init__(self, *args, **kwargs):
397398
self.requested_port = 0
399+
self.mappath = {}
398400
super().__init__(*args, **kwargs)
399401

400402
def initialize(self, state):
@@ -490,6 +492,11 @@ async def ensure_process(self):
490492
async def proxy(self, port, path):
491493
if not path.startswith('/'):
492494
path = '/' + path
495+
if self.mappath:
496+
if callable(self.mappath):
497+
path = call_with_asked_args(self.mappath, {'path': path})
498+
else:
499+
path = self.mappath.get(path, path)
493500

494501
await self.ensure_process()
495502

jupyterlab-server-proxy/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Launcher icons for proxied applications
1010
## Installation
1111

1212
```bash
13-
jupyter labextension install jupyterlab-server-proxy
13+
jupyter labextension install @jupyterlab/server-proxy
1414
```
1515

1616
## Development

jupyterlab-server-proxy/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"name": "jupyterlab-server-proxy",
2+
"name": "@jupyterlab/server-proxy",
33
"version": "2.0.0",
44
"description": "Launcher icons for proxied applications",
55
"keywords": [

tests/resources/jupyter_server_config.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
def mappathf(path):
2+
p = path + 'mapped'
3+
return p
4+
15
c.ServerProxy.servers = {
26
'python-http': {
37
'command': ['python3', './tests/resources/httpinfo.py', '{port}'],
@@ -10,6 +14,16 @@
1014
'command': ['python3', './tests/resources/httpinfo.py', '{port}'],
1115
'port': 54321,
1216
},
17+
'python-http-mappath': {
18+
'command': ['python3', './tests/resources/httpinfo.py', '{port}'],
19+
'mappath': {
20+
'/': '/index.html',
21+
}
22+
},
23+
'python-http-mappathf': {
24+
'command': ['python3', './tests/resources/httpinfo.py', '{port}'],
25+
'mappath': mappathf,
26+
},
1327
}
1428

1529
import sys

tests/test_proxies.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import os
22
from http.client import HTTPConnection
3+
import pytest
34

45
PORT = os.getenv('TEST_PORT', 8888)
56
TOKEN = os.getenv('JUPYTER_TOKEN', 'secret')
67

78

89
def request_get(port, path, token, host='localhost'):
910
h = HTTPConnection(host, port, 10)
10-
h.request('GET', '{}?token={}'.format(path, token))
11+
if '?' in path:
12+
url = '{}&token={}'.format(path, token)
13+
else:
14+
url = '{}?token={}'.format(path, token)
15+
h.request('GET', url)
1116
return h.getresponse()
1217

1318

@@ -58,6 +63,39 @@ def test_server_proxy_port_absolute():
5863
assert 'X-Forwarded-Context' not in s
5964
assert 'X-Proxycontextpath' not in s
6065

66+
67+
@pytest.mark.parametrize(
68+
"requestpath,expected", [
69+
('/', '/index.html?token='),
70+
('/?q=1', '/index.html?q=1&token='),
71+
('/pqr?q=2', '/pqr?q=2&token='),
72+
]
73+
)
74+
def test_server_proxy_mappath_dict(requestpath, expected):
75+
r = request_get(PORT, '/python-http-mappath' + requestpath, TOKEN)
76+
assert r.code == 200
77+
s = r.read().decode('ascii')
78+
assert s.startswith('GET ' + expected)
79+
assert 'X-Forwarded-Context: /python-http-mappath\n' in s
80+
assert 'X-Proxycontextpath: /python-http-mappath\n' in s
81+
82+
83+
@pytest.mark.parametrize(
84+
"requestpath,expected", [
85+
('/', '/mapped?token='),
86+
('/?q=1', '/mapped?q=1&token='),
87+
('/stu?q=2', '/stumapped?q=2&token='),
88+
]
89+
)
90+
def test_server_proxy_mappath_callable(requestpath, expected):
91+
r = request_get(PORT, '/python-http-mappathf' + requestpath, TOKEN)
92+
assert r.code == 200
93+
s = r.read().decode('ascii')
94+
assert s.startswith('GET ' + expected)
95+
assert 'X-Forwarded-Context: /python-http-mappathf\n' in s
96+
assert 'X-Proxycontextpath: /python-http-mappathf\n' in s
97+
98+
6199
def test_server_proxy_remote():
62100
r = request_get(PORT, '/newproxy', TOKEN, host='127.0.0.1')
63101
assert r.code == 200

0 commit comments

Comments
 (0)