(websocket: WebSocket)
| 704 | return None |
| 705 | |
| 706 | async def websocket_handler(websocket: WebSocket): |
| 707 | # Validate Origin header to prevent Cross-Site WebSocket Hijacking |
| 708 | origin = websocket.headers.get("origin") |
| 709 | host = websocket.headers.get("host") |
| 710 | error = validate_origin(origin, host) |
| 711 | if error: |
| 712 | await websocket.close(code=4003, reason=error) |
| 713 | return |
| 714 | |
| 715 | # Call websocket_connect hooks (before accept) |
| 716 | # pylint: disable=protected-access |
| 717 | rejection = await self._run_ws_hooks( |
| 718 | dash_app._hooks.get_hooks("websocket_connect"), |
| 719 | websocket, |
| 720 | default_reason="Connection rejected", |
| 721 | ) |
| 722 | if rejection: |
| 723 | await websocket.close(code=rejection[0], reason=rejection[1]) |
| 724 | return |
| 725 | |
| 726 | await websocket.accept() |
| 727 | |
| 728 | # Create janus queue for outbound messages (main loop context) |
| 729 | outbound_queue: janus.Queue[str] = janus.Queue() |
| 730 | # Track pending get_props requests with standard queue.Queue for responses |
| 731 | pending_get_props: Dict[str, queue.Queue] = {} |
| 732 | # Shutdown event to signal connection closure to worker threads |
| 733 | shutdown_event = threading.Event() |
| 734 | # Get thread pool executor |
| 735 | executor = self.get_callback_executor() |
| 736 | # Track pending callback futures |
| 737 | pending_callbacks: Dict[str, concurrent.futures.Future] = {} |
| 738 | |
| 739 | # Start sender task to drain outbound queue (sends pre-serialized text) |
| 740 | # pylint: disable=protected-access |
| 741 | batch_delay = getattr(dash_app, "_websocket_batch_delay", 0.005) |
| 742 | sender_task = asyncio.create_task( |
| 743 | run_ws_sender(websocket.send_text, outbound_queue, batch_delay) |
| 744 | ) |
| 745 | |
| 746 | try: |
| 747 | while True: |
| 748 | message = await websocket.receive_json() |
| 749 | |
| 750 | # Call websocket_message hooks |
| 751 | rejection = await self._run_ws_hooks( |
| 752 | dash_app._hooks.get_hooks("websocket_message"), |
| 753 | websocket, |
| 754 | message, |
| 755 | default_reason="Message rejected", |
| 756 | ) |
| 757 | if rejection: |
| 758 | await websocket.close(code=rejection[0], reason=rejection[1]) |
| 759 | return |
| 760 | |
| 761 | msg_type = message.get("type") |
| 762 | |
| 763 | if msg_type == "callback_request": |
nothing calls this directly
no test coverage detected