|
1 |
| -from __future__ import annotations |
2 |
| - |
3 |
| -import argparse |
4 |
| -import logging |
5 |
| -import os |
6 |
| -from urllib.parse import urlparse |
7 |
| - |
8 |
| -from tornado import ioloop |
9 |
| -from tornado.httpserver import HTTPServer |
10 |
| -from tornado.log import app_log as log |
11 |
| -from tornado.log import enable_pretty_logging, gen_log |
12 |
| - |
13 |
| -from .activity import start_activity_update |
14 |
| -from .proxy import configure_ssl, make_proxy_app |
15 |
| - |
16 |
| - |
17 |
| -def _default_address_and_port() -> tuple[str, int]: |
18 |
| - """ |
19 |
| - Get the Address and Port for the Proxy, either from JUPYTERHUB_SERVICE_URL or default values. |
20 |
| - See https://github.com/jupyterhub/jupyterhub/blob/4.x/jupyterhub/singleuser/mixins.py#L266-L284. |
21 |
| - """ |
22 |
| - address = "127.0.0.1" |
23 |
| - port = 8888 |
24 |
| - |
25 |
| - if os.environ.get("JUPYTERHUB_SERVICE_URL"): |
26 |
| - url = urlparse(os.environ["JUPYTERHUB_SERVICE_URL"]) |
27 |
| - |
28 |
| - if url.hostname: |
29 |
| - address = url.hostname |
30 |
| - |
31 |
| - if url.port: |
32 |
| - port = url.port |
33 |
| - elif url.scheme == "http": |
34 |
| - port = 80 |
35 |
| - elif url.scheme == "https": |
36 |
| - port = 443 |
37 |
| - |
38 |
| - return address, port |
39 |
| - |
40 |
| - |
41 |
| -def run( |
42 |
| - command: list[str], |
43 |
| - port: int | None, |
44 |
| - address: str | None, |
45 |
| - server_port: int, |
46 |
| - socket_path: str | None, |
47 |
| - socket_auto: bool, |
48 |
| - environment: list[tuple[str, str]] | None, |
49 |
| - mappath: list[tuple[str, str]] | None, |
50 |
| - debug: bool, |
51 |
| - # logs: bool, |
52 |
| - skip_authentication: bool, |
53 |
| - timeout: int, |
54 |
| - activity_interval: int, |
55 |
| - # progressive: bool, |
56 |
| - websocket_max_message_size: int, |
57 |
| -): |
58 |
| - # Setup Logging |
59 |
| - enable_pretty_logging(logger=log) |
60 |
| - if debug: |
61 |
| - log.setLevel(logging.DEBUG) |
62 |
| - gen_log.setLevel(logging.DEBUG) |
63 |
| - |
64 |
| - address_port_default = _default_address_and_port() |
65 |
| - address = address or address_port_default[0] |
66 |
| - port = port or address_port_default[1] |
67 |
| - |
68 |
| - if skip_authentication: |
69 |
| - log.warn("Disabling Authentication with JupyterHub Server!") |
70 |
| - |
71 |
| - prefix = os.environ.get("JUPYTERHUB_SERVICE_PREFIX", "/") |
72 |
| - |
73 |
| - app = make_proxy_app( |
74 |
| - command, |
75 |
| - prefix.removesuffix("/"), |
76 |
| - server_port, |
77 |
| - socket_path or socket_auto, |
78 |
| - dict(environment), |
79 |
| - dict(mappath), |
80 |
| - timeout, |
81 |
| - skip_authentication, |
82 |
| - debug, |
83 |
| - # progressive, |
84 |
| - websocket_max_message_size, |
85 |
| - ) |
86 |
| - |
87 |
| - ssl_options = configure_ssl() |
88 |
| - http_server = HTTPServer(app, ssl_options=ssl_options, xheaders=True) |
89 |
| - http_server.listen(port, address) |
90 |
| - |
91 |
| - log.info(f"Starting standaloneproxy on '{address}:{port}'") |
92 |
| - log.info(f"URL Prefix: {prefix!r}") |
93 |
| - log.info(f"Command: {' '.join(command)!r}") |
94 |
| - |
95 |
| - # Periodically send JupyterHub Notifications, that we are still running |
96 |
| - if activity_interval > 0: |
97 |
| - log.info( |
98 |
| - f"Sending Activity Notification to JupyterHub with interval={activity_interval}s" |
99 |
| - ) |
100 |
| - start_activity_update(activity_interval) |
101 |
| - |
102 |
| - ioloop.IOLoop.current().start() |
| 1 | +from .app import StandaloneProxyServer |
103 | 2 |
|
104 | 3 |
|
105 | 4 | def main():
|
106 |
| - parser = argparse.ArgumentParser( |
107 |
| - "jupyter-standalone-proxy", |
108 |
| - description="Wrap an arbitrary web service so it can be used in place of 'jupyterhub-singleuser' in a JupyterHub setting.", |
109 |
| - formatter_class=argparse.ArgumentDefaultsHelpFormatter, |
110 |
| - ) |
111 |
| - |
112 |
| - parser.add_argument( |
113 |
| - "-p", |
114 |
| - "--port", |
115 |
| - type=int, |
116 |
| - dest="port", |
117 |
| - help="Set port for the proxy server to listen on. Will use 'JUPYTERHUB_SERVICE_URL' or '8888' by default.", |
118 |
| - ) |
119 |
| - parser.add_argument( |
120 |
| - "-a", |
121 |
| - "--address", |
122 |
| - type=str, |
123 |
| - dest="address", |
124 |
| - help="Set address for the proxy server to listen on. Will use 'JUPYTERHUB_SERVICE_URL' or '127.0.0.1' by default.", |
125 |
| - ) |
126 |
| - parser.add_argument( |
127 |
| - "-s", |
128 |
| - "--server-port", |
129 |
| - default=0, |
130 |
| - type=int, |
131 |
| - dest="server_port", |
132 |
| - help="Port for the web service should end up running on (0 for random open port).", |
133 |
| - ) |
134 |
| - parser.add_argument( |
135 |
| - "--socket-path", |
136 |
| - type=str, |
137 |
| - default=None, |
138 |
| - help="Path to the Unix Socket to use for proxying. Takes precedence over '-s/--server_port' and '--socket-auto'.", |
139 |
| - ) |
140 |
| - parser.add_argument( |
141 |
| - "--socket-auto", |
142 |
| - action="store_true", |
143 |
| - help="Use Unix Socket for proxying, but let jupyter-server-proxy automatically create one.", |
144 |
| - ) |
145 |
| - parser.add_argument( |
146 |
| - "--env", |
147 |
| - "--environment", |
148 |
| - type=lambda v: tuple(v.split(":")[:2]), |
149 |
| - default=[], |
150 |
| - action="append", |
151 |
| - dest="environment", |
152 |
| - help="Add an environment variable to the server process. Must be of the form <Name>:<Value>, e.g. --env=MY_VAR:42", |
153 |
| - ) |
154 |
| - parser.add_argument( |
155 |
| - "--mappath", |
156 |
| - type=lambda v: tuple(v.split(":")[:2]), |
157 |
| - default=[], |
158 |
| - action="append", |
159 |
| - help="Add an path mapping to the proxy. Any requests received under <Source> will be redirected to <Target>. " |
160 |
| - "Must be of the form <Source>:<Target>, e.g. --mappath=/:/index.html", |
161 |
| - ) |
162 |
| - parser.add_argument( |
163 |
| - "-d", |
164 |
| - "--debug", |
165 |
| - action="store_true", |
166 |
| - default=False, |
167 |
| - dest="debug", |
168 |
| - help="Display debug level logs.", |
169 |
| - ) |
170 |
| - # ToDo: Split Server and Application Logger |
171 |
| - # parser.add_argument( |
172 |
| - # "--logs", |
173 |
| - # action="store_true", |
174 |
| - # default=True, |
175 |
| - # help="Display logs generated by the subprocess.", |
176 |
| - # ) |
177 |
| - parser.add_argument( |
178 |
| - "--skip-authentication", |
179 |
| - action="store_true", |
180 |
| - help="Do not enforce authentication with the JupyterHub Server.", |
181 |
| - ) |
182 |
| - parser.add_argument( |
183 |
| - "--timeout", |
184 |
| - default=60, |
185 |
| - type=int, |
186 |
| - help="Timeout to wait until the subprocess has started and can be addressed.", |
187 |
| - ) |
188 |
| - parser.add_argument( |
189 |
| - "--activity-interval", |
190 |
| - default=300, |
191 |
| - type=int, |
192 |
| - help="Frequency to notify Hub that the service is still running (In seconds, 0 for never).", |
193 |
| - ) |
194 |
| - # ToDo: Progressive Proxy |
195 |
| - # parser.add_argument( |
196 |
| - # "--progressive", |
197 |
| - # action="store_true", |
198 |
| - # default=False, |
199 |
| - # help="Progressively flush responses as they arrive (good for Voila).", |
200 |
| - # ) |
201 |
| - parser.add_argument( |
202 |
| - "--websocket-max-message-size", |
203 |
| - default=0, |
204 |
| - type=int, |
205 |
| - help="Max size of websocket data (leave at 0 for library defaults).", |
206 |
| - ) |
207 |
| - parser.add_argument( |
208 |
| - "command", nargs="+", help="The command executed for starting the web service." |
209 |
| - ) |
210 |
| - |
211 |
| - args = parser.parse_args() |
212 |
| - run(**vars(args)) |
| 5 | + StandaloneProxyServer.launch_instance() |
213 | 6 |
|
214 | 7 |
|
215 | 8 | if __name__ == "__main__":
|
|
0 commit comments