ASGI send callable.
(self, message)
| 130 | return await self._receive_queue.get() |
| 131 | |
| 132 | async def _send(self, message): |
| 133 | """ASGI send callable.""" |
| 134 | msg_type = message["type"] |
| 135 | |
| 136 | if msg_type == "websocket.accept": |
| 137 | if self.accepted: |
| 138 | raise RuntimeError("WebSocket already accepted") |
| 139 | await self._send_accept(message) |
| 140 | self.accepted = True |
| 141 | |
| 142 | elif msg_type == "websocket.send": |
| 143 | if not self.accepted: |
| 144 | raise RuntimeError("WebSocket not accepted") |
| 145 | if self.closed: |
| 146 | raise RuntimeError("WebSocket closed") |
| 147 | |
| 148 | # Check for truthy values since both keys may be present with None |
| 149 | text = message.get("text") |
| 150 | bytes_data = message.get("bytes") |
| 151 | if text is not None: |
| 152 | await self._send_frame(OPCODE_TEXT, text.encode("utf-8")) |
| 153 | elif bytes_data is not None: |
| 154 | await self._send_frame(OPCODE_BINARY, bytes_data) |
| 155 | |
| 156 | elif msg_type == "websocket.close": |
| 157 | code = message.get("code", CLOSE_NORMAL) |
| 158 | reason = message.get("reason", "") |
| 159 | await self._send_close(code, reason) |
| 160 | |
| 161 | # Wait for client's close frame (RFC 6455 close handshake) |
| 162 | try: |
| 163 | await asyncio.wait_for(self._close_event.wait(), timeout=5.0) |
| 164 | except asyncio.TimeoutError: |
| 165 | self.log.debug("WebSocket close handshake timeout") |
| 166 | self.closed = True |
| 167 | self._close_event.set() |
| 168 | |
| 169 | # Close the transport after close handshake |
| 170 | self.transport.close() |
| 171 | |
| 172 | async def _send_accept(self, message): |
| 173 | """Send WebSocket handshake accept response.""" |