1616from traitlets .config import Configurable
1717
1818from .handlers import AddSlashHandler , NamedLocalProxyHandler , SuperviseAndProxyHandler
19+ from .rawsocket import RawSocketHandler , SuperviseAndRawSocketHandler
1920
2021try :
2122 # Traitlets >= 4.3.3
4344 "request_headers_override" ,
4445 "rewrite_response" ,
4546 "update_last_activity" ,
47+ "raw_socket_proxy" ,
4648 ],
4749)
4850
4951
50- def _make_namedproxy_handler (sp : ServerProcess ):
51- class _Proxy (NamedLocalProxyHandler ):
52- def __init__ (self , * args , ** kwargs ):
53- super ().__init__ (* args , ** kwargs )
54- self .name = sp .name
55- self .proxy_base = sp .name
56- self .absolute_url = sp .absolute_url
57- self .port = sp .port
58- self .unix_socket = sp .unix_socket
59- self .mappath = sp .mappath
60- self .rewrite_response = sp .rewrite_response
61- self .update_last_activity = sp .update_last_activity
62-
63- def get_request_headers_override (self ):
64- return self ._realize_rendered_template (sp .request_headers_override )
65-
66- return _Proxy
67-
68-
69- def _make_supervisedproxy_handler (sp : ServerProcess ):
52+ def _make_proxy_handler (sp : ServerProcess ):
7053 """
71- Create a SuperviseAndProxyHandler subclass with given parameters
54+ Create an appropriate handler with given parameters
7255 """
56+ if sp .command :
57+ cls = SuperviseAndRawSocketHandler if sp .raw_socket_proxy else SuperviseAndProxyHandler
58+ args = dict (state = {})
59+ elif not (sp .port or isinstance (sp .unix_socket , str )):
60+ warn (
61+ f"Server proxy { sp .name } does not have a command, port "
62+ f"number or unix_socket path. At least one of these is "
63+ f"required."
64+ )
65+ return
66+ else :
67+ cls = RawSocketHandler if sp .raw_socket_proxy else NamedLocalProxyHandler
68+ args = {}
7369
7470 # FIXME: Set 'name' properly
75- class _Proxy (SuperviseAndProxyHandler ):
71+ class _Proxy (cls ):
72+ kwargs = args
73+
7674 def __init__ (self , * args , ** kwargs ):
7775 super ().__init__ (* args , ** kwargs )
7876 self .name = sp .name
7977 self .command = sp .command
8078 self .proxy_base = sp .name
8179 self .absolute_url = sp .absolute_url
82- self .requested_port = sp .port
83- self .requested_unix_socket = sp .unix_socket
80+ if sp .command :
81+ self .requested_port = sp .port
82+ self .requested_unix_socket = sp .unix_socket
83+ else :
84+ self .port = sp .port
85+ self .unix_socket = sp .unix_socket
8486 self .mappath = sp .mappath
8587 self .rewrite_response = sp .rewrite_response
8688 self .update_last_activity = sp .update_last_activity
8789
88- def get_env (self ):
89- return self ._realize_rendered_template (sp .environment )
90-
9190 def get_request_headers_override (self ):
9291 return self ._realize_rendered_template (sp .request_headers_override )
9392
93+ # these two methods are only used in supervise classes, but do no harm otherwise
94+ def get_env (self ):
95+ return self ._realize_rendered_template (sp .environment )
96+
9497 def get_timeout (self ):
9598 return sp .timeout
9699
@@ -116,24 +119,14 @@ def make_handlers(base_url, server_processes):
116119 """
117120 handlers = []
118121 for sp in server_processes :
119- if sp .command :
120- handler = _make_supervisedproxy_handler (sp )
121- kwargs = dict (state = {})
122- else :
123- if not (sp .port or isinstance (sp .unix_socket , str )):
124- warn (
125- f"Server proxy { sp .name } does not have a command, port "
126- f"number or unix_socket path. At least one of these is "
127- f"required."
128- )
129- continue
130- handler = _make_namedproxy_handler (sp )
131- kwargs = {}
122+ handler = _make_proxy_handler (sp )
123+ if not handler :
124+ continue
132125 handlers .append (
133126 (
134127 ujoin (base_url , sp .name , r"(.*)" ),
135128 handler ,
136- kwargs ,
129+ handler . kwargs
137130 )
138131 )
139132 handlers .append ((ujoin (base_url , sp .name ), AddSlashHandler ))
@@ -169,6 +162,7 @@ def make_server_process(name, server_process_config, serverproxy_config):
169162 update_last_activity = server_process_config .get (
170163 "update_last_activity" , True
171164 ),
165+ raw_socket_proxy = server_process_config .get ("raw_socket_proxy" , False ),
172166 )
173167
174168
@@ -292,6 +286,12 @@ def cats_only(response, path):
292286
293287 update_last_activity
294288 Will cause the proxy to report activity back to jupyter server.
289+
290+ raw_socket_proxy
291+ Proxy websocket requests as a raw TCP (or unix socket) stream.
292+ In this mode, only websockets are handled, and messages are sent to the backend,
293+ similar to running a websockify layer (https://github.com/novnc/websockify).
294+ All other HTTP requests return 405 (and thus this will also bypass rewrite_response).
295295 """ ,
296296 config = True ,
297297 )
0 commit comments