-
Notifications
You must be signed in to change notification settings - Fork 147
/
Copy pathserver-process.md
326 lines (239 loc) · 11.1 KB
/
server-process.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
(server-process)=
# Starting & Proxying processes
Jupyter Server Proxy can start & supervise the process providing
the web service it is proxying. The process is started the first
time an appropriate URL is requested, and restarted if it fails.
Processes that are supervised and proxied are called **servers**.
They can be configured either in the notebook configuration, or
as separate packages.
## Server Process options
Server Processes are configured with a dictionary of key value
pairs.
(server-process:cmd)=
### `command`
One of:
- A list of strings that is the command used to start the
process. The following template strings will be replaced:
- `{port}` the port that the process should listen on. This will be 0 if it
should use a Unix socket instead.
- `{unix_socket}` the path at which the process should listen on a Unix
socket. This will be an empty string if it should use a TCP port.
- `{base_url}` the base URL of the notebook. For example, if the application
needs to know its full path it can be constructed from
`{base_url}/proxy/{port}`
- A callable that takes any {ref}`callable arguments <server-process:callable-arguments>`,
and returns a list of strings that are used & treated same as above.
If the command is not specified or is an empty list, the server process is
assumed to be started ahead of time and already available to be proxied to.
### `timeout`
Timeout in seconds for the process to become ready, default `5`.
A process is considered 'ready' when it can return a valid HTTP response on the
port it is supposed to start at.
### `environment`
One of:
- A dictionary of strings that are passed in as the environment to
the started process, in addition to the environment of the notebook
process itself. The strings `{port}`, `{unix_socket}` and
`{base_url}` will be replaced as for **command**.
- A callable that takes any {ref}`callable arguments <server-process:callable-arguments>`,
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:
```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.
Defaults to _False_.
### `port`
Set the port that the service will listen on. The default is to
automatically select an unused port.
(server-process:unix-socket)=
### `unix_socket`
This option uses a Unix socket on a filesystem path, instead of a TCP
port. It can be passed as a string specifying the socket path, or _True_ for
Jupyter Server Proxy to create a temporary directory to hold the socket,
ensuring that only the user running Jupyter can connect to it.
If this is used, the `{unix_socket}` argument in the command template
(see {ref}`server-process:cmd`) will be a filesystem path. The server should
create a Unix socket bound to this path and listen for HTTP requests on it.
The `port` configuration key will be ignored.
```{note}
Proxying websockets over a Unix socket requires Tornado >= 6.3.
```
### `mappath`
Map request paths to proxied paths.
Either a dictionary of request paths to proxied paths,
or a callable that takes parameter `path` and returns the proxied path.
### `launcher_entry`
A dictionary with options on if / how an entry in the classic Jupyter Notebook
'New' dropdown or the JupyterLab launcher should be added. It can contain
the following keys:
1. **enabled**
Set to True (default) to make an entry in the launchers. Set to False to have no
explicit entry.
2. **icon_path**
Full path to an svg icon that could be used with a launcher. Currently only used by the
JupyterLab launcher, when category is "Notebook" (default) or "Console".
3. **title**
Title to be used for the launcher entry. Defaults to the name of the server if missing.
4. **path_info**
The trailing path that is appended to the user's server URL to access the proxied server.
By default it is the name of the server followed by a trailing slash.
5. **category**
The category for the launcher item. Currently only used by the JupyterLab launcher.
By default it is "Notebook".
### `new_browser_tab`
_JupyterLab only_ - _True_ (default) if the proxied server URL should be opened in a new browser tab.
_False_ if the proxied server URL should be opened in a new JupyterLab tab.
If _False_, the proxied server needs to allow its pages to be rendered in an iframe. This
is generally done by configuring the web server `X-Frame-Options` to `SAMEORIGIN`.
For more information, refer to
[MDN Web docs on X-Frame-Options](https://developer.mozilla.org/docs/Web/HTTP/Headers/X-Frame-Options).
Note that applications might use a different terminology to refer to frame options.
For example, RStudio uses the term _frame origin_ and require the flag
`--www-frame-origin=same` to allow rendering of its pages in an iframe.
### `request_headers_override`
One of:
- A dictionary of strings that are passed in as HTTP headers to the proxy
request. The strings `{port}`, `{unix_socket}` and `{base_url}` will be
replaced as for **command**.
- A callable that takes any {ref}`callable arguments <server-process:callable-arguments>`,
and returns a dictionary of strings that are used & treated same as above.
### `update_last_activity`
Whether to report activity from the proxy to Jupyter Server. If _True_, Jupyter Server
will be notified of new activity. This is primarily used by JupyterHub for idle detection and culling.
Useful if you want to have a seperate way of determining activity through a
proxied application.
Defaults to _True_.
(server-process:callable-arguments)=
### `raw_socket_proxy`
_True_ to proxy only websocket connections into raw stream connections.
_False_ (default) if the proxied server speaks full HTTP.
If _True_, the proxied server is treated a raw TCP (or unix socket) server that
does not use HTTP.
In this mode, only websockets are handled, and messages are sent to the backend
server as raw stream data. This is similar to running a
[websockify](https://github.com/novnc/websockify) wrapper.
All other HTTP requests return 405.
### Callable arguments
Certain config options accept callables, as documented above. This should return
the same type of object that the option normally expects.
When you use a callable this way, it can ask for any arguments it needs
by simply declaring it - only arguments the callable asks for will be passed to it.
For example, with the following config:
```python
def _cmd_callback():
return ["some-command"]
server_config = {
"command": _cmd_callback
}
```
No arguments will be passed to `_cmd_callback`, since it doesn't ask for any. However,
with:
```python
def _cmd_callback(port):
return ["some-command", "--port=" + str(port)]
server_config = {
"command": _cmd_callback
}
```
The `port` argument will be passed to the callable. This is a simple form of dependency
injection that helps us add more parameters in the future without breaking backwards
compatibility.
#### Available arguments
Unless otherwise documented for specific options, the arguments available for
callables are:
1. **port**
The TCP port on which the server should listen, or is listening.
This is 0 if a Unix socket is used instead of TCP.
2. **unix_socket**
The path of a Unix socket on which the server should listen, or is listening.
This is an empty string if a TCP socket is used.
3. **base_url**
The base URL of the notebook
If any of the returned strings, lists or dictionaries contain strings
of form `{<argument-name>}`, they will be replaced with the value
of the argument. For example, if your function is:
```python
def _openrefine_cmd():
return ["openrefine", "-p", "{port}"]
```
The `{port}` will be replaced with the appropriate port before
the command is started
## Specifying config via traitlets
[Traitlets](https://traitlets.readthedocs.io/) are the configuration
mechanism used by Jupyter Notebook. It can take config in Python
and we can use that to specify Server Processes - including functions
if we want tighter control over what process is spawned.
1. Create a file called `jupyter_server_config.py` in one of the
Jupyter config directories. You can get a list of these directories
by running `jupyter --paths` and looking under the 'config'
section
2. Add your Server Process configuration there by setting
`c.ServerProxy.servers` traitlet.
For example,
```python
c.ServerProxy.servers = {
"openrefine": {
"command": ["refine", "-p", "{port}"]
}
}
```
This will start [OpenRefine](https://openrefine.org/) with the
`refine` command (which must be in \$PATH) on a randomly
generated port, and make it available under `/openrefine`
in your notebook url. The URL path is specified by the key,
but this should be made more configurable in the future.
(server-process:package)=
## Specifying config from python packages
It is often convenient to provide the Server Process configuration
as a python package, so users can simply `pip install` it.
This is possible, thanks to [the magic of entrypoints](https://amir.rachum.com/blog/2017/07/28/python-entry-points/).
We'll work through it by repeating the OpenRefine example from
above.
1. Create a python file named `openrefine.py`
```python
def setup_openrefine():
return {
"command": ["refine", "-p", "{port}"]
}
```
A simple function that returns a Server Process configuration
dictionary when called. This can return any kind of Server
Process configuration dictionary, and include functions easily.
2. Make an appropriate `setup.py`
```python
import setuptools
setuptools.setup(
name="jupyter-openrefine-server",
# py_modules rather than packages, since we only have 1 file
py_modules=["openrefine"],
entry_points={
"jupyter_serverproxy_servers": [
# name = packagename:function_name
"openrefine = openrefine:setup_openrefine",
]
},
install_requires=["jupyter-server-proxy"],
)
```
We make an entry for the `jupyter_serverproxy_servers` entrypoint.
When jupyter-server-proxy starts up, it goes through the list of
entrypoint entries from all installed packages & sets itself up
with all the Server Process configurations.
3. You can now test this out with `pip install .`, making sure you
are in the same environment as the jupyter notebook process. If you
go to `<notebook-url>/openrefine` (and have OpenRefine installed
and in `$PATH`!), you should see an instance of OpenRefine!