@@ -70,7 +70,7 @@ def reload_end():
7070
7171 async def _ () -> None :
7272 options = {
73- "extra_headers " : {
73+ "additional_headers " : {
7474 "Shiny-Autoreload-Secret" : os .getenv ("SHINY_AUTORELOAD_SECRET" , "" ),
7575 }
7676 }
@@ -186,6 +186,8 @@ async def _coro_main(
186186 port : int , app_url : str , secret : str , launch_browser : bool
187187) -> None :
188188 import websockets
189+ import websockets .asyncio .server
190+ import websockets .http11
189191
190192 reload_now : asyncio .Event = asyncio .Event ()
191193
@@ -198,18 +200,22 @@ def nudge():
198200 reload_now .set ()
199201 reload_now .clear ()
200202
201- async def reload_server (conn : websockets .server .WebSocketServerProtocol ):
203+ async def reload_server (conn : websockets .asyncio . server .ServerConnection ):
202204 try :
203- if conn .path == "/autoreload" :
205+ if conn .request is None :
206+ raise RuntimeError (
207+ "Autoreload server received a connection with no request"
208+ )
209+ elif conn .request .path == "/autoreload" :
204210 # The client wants to be notified when the app has reloaded. The client
205211 # in this case is the web browser, specifically shiny-autoreload.js.
206212 while True :
207213 await reload_now .wait ()
208214 await conn .send ("autoreload" )
209- elif conn .path == "/notify" :
215+ elif conn .request . path == "/notify" :
210216 # The client is notifying us that the app has reloaded. The client in
211217 # this case is the uvicorn worker process (see reload_end(), above).
212- req_secret = conn .request_headers .get ("Shiny-Autoreload-Secret" , "" )
218+ req_secret = conn .request . headers .get ("Shiny-Autoreload-Secret" , "" )
213219 if req_secret != secret :
214220 # The client couldn't prove that they were from a child process
215221 return
@@ -224,7 +230,7 @@ async def reload_server(conn: websockets.server.WebSocketServerProtocol):
224230 # about only WebSockets being supported. This is not an academic problem as the
225231 # VSCode extension used in RSW sniffs out ports that are being listened on, which
226232 # leads to confusion if all you get is an error.
227- async def process_request (
233+ async def process_request_legacy (
228234 path : str , request_headers : websockets .datastructures .Headers
229235 ) -> Optional [tuple [http .HTTPStatus , websockets .datastructures .HeadersLike , bytes ]]:
230236 # If there's no Upgrade header, it's not a WebSocket request.
@@ -236,8 +242,22 @@ async def process_request(
236242 await asyncio .sleep (1 )
237243 return (http .HTTPStatus .MOVED_PERMANENTLY , [("Location" , app_url )], b"" )
238244
245+ async def process_request_new (
246+ connection : websockets .asyncio .server .ServerConnection ,
247+ request : websockets .http11 .Request ,
248+ ) -> websockets .http11 .Response | None :
249+ if request .headers .get ("Upgrade" ) is None :
250+ return websockets .http11 .Response (
251+ status_code = http .HTTPStatus .MOVED_PERMANENTLY ,
252+ reason_phrase = "Moved Permanently" ,
253+ headers = websockets .Headers (Location = app_url ),
254+ body = None ,
255+ )
256+ else :
257+ return None
258+
239259 async with websockets .serve (
240- reload_server , "127.0.0.1" , port , process_request = process_request
260+ reload_server , "127.0.0.1" , port , process_request = process_request_new
241261 ):
242262 await asyncio .Future () # wait forever
243263
0 commit comments