|
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