When establishing a WebSocket connection over SSL, if the server process is abruptly terminated via kill -9, the reverse proxy cannot gracefully handle the exception.
$ python ./ws-rs.py
INFO: Started server process [31689]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8765 (Press CTRL+C to quit)
INFO: ('127.0.0.1', 56556) - "WebSocket /ws" [accepted]
Exception is not set. when close ws connection. client: <Task pending name='client_to_server_task' coro=<_wait_client_then_send_to_server() running at /root/venv/lib/python3.12/site-packages/fastapi_proxy_lib/core/websocket.py:281> wait_for=<Future pending cb=[Task.task_wakeup()]>>, server:<Task pending name='server_to_client_task' coro=<_wait_server_then_send_to_client() running at /root/venv/lib/python3.12/site-packages/fastapi_proxy_lib/core/websocket.py:306> wait_for=<Future pending cb=[Task.task_wakeup()]>>
ERROR: Exception in ASGI application
+ Exception Group Traceback (most recent call last):
| File "/root/venv/lib/python3.12/site-packages/uvicorn/protocols/websockets/wsproto_impl.py", line 235, in run_asgi
| result = await self.app(self.scope, self.receive, self.send) # type: ignore[func-returns-value]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/root/venv/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 60, in __call__
| return await self.app(scope, receive, send)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/root/venv/lib/python3.12/site-packages/fastapi/applications.py", line 1054, in __call__
| await super().__call__(scope, receive, send)
| File "/root/venv/lib/python3.12/site-packages/starlette/applications.py", line 113, in __call__
| await self.middleware_stack(scope, receive, send)
| File "/root/venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 152, in __call__
| await self.app(scope, receive, send)
| File "/root/venv/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 62, in __call__
| await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
| File "/root/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
| raise exc
| File "/root/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app
| await app(scope, receive, sender)
| File "/root/venv/lib/python3.12/site-packages/starlette/routing.py", line 715, in __call__
| await self.middleware_stack(scope, receive, send)
| File "/root/venv/lib/python3.12/site-packages/starlette/routing.py", line 735, in app
| await route.handle(scope, receive, send)
| File "/root/venv/lib/python3.12/site-packages/starlette/routing.py", line 362, in handle
| await self.app(scope, receive, send)
| File "/root/venv/lib/python3.12/site-packages/starlette/routing.py", line 95, in app
| await wrap_app_handling_exceptions(app, session)(scope, receive, send)
| File "/root/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
| raise exc
| File "/root/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app
| await app(scope, receive, sender)
| File "/root/venv/lib/python3.12/site-packages/starlette/routing.py", line 93, in app
| await func(session)
| File "/root/venv/lib/python3.12/site-packages/fastapi/routing.py", line 383, in app
| await dependant.call(**solved_result.values)
| File "/root/venv/lib/python3.12/site-packages/fastapi_proxy_lib/fastapi/router.py", line 112, in ws_proxy
| return await proxy.proxy(websocket=websocket, path=path)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/root/venv/lib/python3.12/site-packages/fastapi_proxy_lib/core/websocket.py", line 806, in proxy
| return await self.send_request_to_target(
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/root/venv/lib/python3.12/site-packages/fastapi_proxy_lib/core/websocket.py", line 567, in send_request_to_target
| async with stack:
| File "/opt/python/lib/python3.12/contextlib.py", line 754, in __aexit__
| raise exc_details[1]
| File "/opt/python/lib/python3.12/contextlib.py", line 737, in __aexit__
| cb_suppress = await cb(*exc_details)
| ^^^^^^^^^^^^^^^^^^^^^^
| File "/opt/python/lib/python3.12/contextlib.py", line 231, in __aexit__
| await self.gen.athrow(value)
| File "/root/venv/lib/python3.12/site-packages/httpx_ws/_api.py", line 1349, in aconnect_ws
| async with _aconnect_ws(
| File "/opt/python/lib/python3.12/contextlib.py", line 231, in __aexit__
| await self.gen.athrow(value)
| File "/root/venv/lib/python3.12/site-packages/httpx_ws/_api.py", line 1254, in _aconnect_ws
| async with session:
| File "/root/venv/lib/python3.12/site-packages/httpx_ws/_api.py", line 658, in __aexit__
| await self._exit_stack.aclose()
| File "/opt/python/lib/python3.12/contextlib.py", line 696, in aclose
| await self.__aexit__(None, None, None)
| File "/opt/python/lib/python3.12/contextlib.py", line 754, in __aexit__
| raise exc_details[1]
| File "/opt/python/lib/python3.12/contextlib.py", line 737, in __aexit__
| cb_suppress = await cb(*exc_details)
| ^^^^^^^^^^^^^^^^^^^^^^
| File "/root/venv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 680, in __aexit__
| raise BaseExceptionGroup(
| ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "/root/venv/lib/python3.12/site-packages/httpx_ws/_api.py", line 1041, in _background_receive
| await self.close(CloseReason.INTERNAL_ERROR, "Stream error")
| File "/root/venv/lib/python3.12/site-packages/httpx_ws/_api.py", line 985, in close
| await self.stream.write(data)
| File "/root/venv/lib/python3.12/site-packages/httpcore/_async/http11.py", line 365, in write
| await self._stream.write(buffer, timeout)
| File "/root/venv/lib/python3.12/site-packages/httpcore/_backends/anyio.py", line 50, in write
| await self._stream.send(item=buffer)
| File "/root/venv/lib/python3.12/site-packages/anyio/streams/tls.py", line 212, in send
| await self._call_sslobject_method(self._ssl_object.write, item)
| File "/root/venv/lib/python3.12/site-packages/anyio/streams/tls.py", line 172, in _call_sslobject_method
| raise EndOfStream from None
| anyio.EndOfStream
+------------------------------------
Exceptions should be gracefully handled, not thrown out (or exposed).
Describe the Bug
When establishing a WebSocket connection over SSL, if the server process is abruptly terminated via
kill -9, the reverse proxy cannot gracefully handle the exception.To Reproduce
$ python ./ws-rs.py INFO: Started server process [31689] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://0.0.0.0:8765 (Press CTRL+C to quit)$ python ./ws-client.py Connected to WebSocket server Sent: Hello, WebSocket Server!Expected Behavior
Exceptions should be gracefully handled, not thrown out (or exposed).
Configuration