fix: close DockerHTTPClient session to prevent UnixConnector leak in Web Wireshark

The Web Wireshark WebSocket endpoint created a WebWiresharkManager but
never called close(), leaving the DockerHTTPClient's ClientSession with
UnixConnector unclosed when users closed the browser tab.

Also switch asyncio.wait in WebSocket proxy from ALL_COMPLETED to
FIRST_COMPLETED to avoid blocking cleanup when one direction disconnects.
This commit is contained in:
YueGuobin 2026-05-18 23:59:56 +08:00
parent c71dfbb6b5
commit 868e317d24
No known key found for this signature in database
2 changed files with 22 additions and 19 deletions

View File

@ -387,27 +387,30 @@ async def web_wireshark_websocket(
# Get container IP
manager = WebWiresharkManager()
container_ip = await manager.get_container_ip(container_name)
try:
container_ip = await manager.get_container_ip(container_name)
if not container_ip:
log.error(f"Container {container_name} not found in wireshark network")
await websocket.close(code=status.WS_1011_INTERNAL_ERROR)
return
if not container_ip:
log.error(f"Container {container_name} not found in wireshark network")
await websocket.close(code=status.WS_1011_INTERNAL_ERROR)
return
# Build container WebSocket URL
container_ws_url = f"ws://{container_ip}:{xpra_port}"
log.info(f"Proxying WebSocket to container: {container_ws_url}")
# Build container WebSocket URL
container_ws_url = f"ws://{container_ip}:{xpra_port}"
log.info(f"Proxying WebSocket to container: {container_ws_url}")
# Get client's requested subprotocols from request headers
scope = websocket.scope
headers = dict(scope.get("headers", []))
requested_protocols_header = headers.get(b"sec-websocket-protocol", b"")
requested_protocols = [p.decode().strip() for p in requested_protocols_header.split(b",") if p.strip()]
log.info(f"Client requested subprotocols: {requested_protocols}")
# Get client's requested subprotocols from request headers
scope = websocket.scope
headers = dict(scope.get("headers", []))
requested_protocols_header = headers.get(b"sec-websocket-protocol", b"")
requested_protocols = [p.decode().strip() for p in requested_protocols_header.split(b",") if p.strip()]
log.info(f"Client requested subprotocols: {requested_protocols}")
# The WebSocket connection has already been accepted by the authentication dependency
# with the proper subprotocol. Now we just proxy data to the backend.
await websocket_proxy(websocket, container_ws_url, requested_protocols)
# The WebSocket connection has already been accepted by the authentication dependency
# with the proper subprotocol. Now we just proxy data to the backend.
await websocket_proxy(websocket, container_ws_url, requested_protocols)
finally:
await manager.close()
except Exception as e:
log.error(f"Error in WebSocket proxy for link {link_id}: {e}")

View File

@ -127,7 +127,7 @@ async def websocket_proxy(
try:
done, pending = await asyncio.wait(
aws,
return_when=asyncio.ALL_COMPLETED
return_when=asyncio.FIRST_COMPLETED
)
except Exception as e:
log.error(f"asyncio.wait raised exception: {e}")
@ -264,7 +264,7 @@ async def websocket_proxy_with_manual_accept(
try:
done, pending = await asyncio.wait(
aws,
return_when=asyncio.ALL_COMPLETED
return_when=asyncio.FIRST_COMPLETED
)
log.info(f"asyncio.wait returned. done={len(done)}, pending={len(pending)}")
except Exception as e: