async def test_startup(self): async with websockets.unix_connect(self.ipc_path) as _ws: await asyncio.sleep(0.01) self.assertEqual(1, len(self.server._connections)) connection = self.server._connections[0] self.assertFalse(connection.request_handler.done()) self.assertFalse(connection.publish_handler.done())
async def hello(): socket_path = os.path.join(os.path.dirname(__file__), "socket") async with websockets.unix_connect(socket_path) as websocket: name = input("What's your name? ") await websocket.send(name) print(f"> {name}") greeting = await websocket.recv() print(f"< {greeting}")
async def test_subscribe_and_unsubscribe(self): feed = TestFeed("foo") self.feed_manager.register_feed(feed) def publish(): feed.publish(1) feed.publish(2) feed.publish(3) async with websockets.unix_connect(self.ipc_path) as ws: asyncio.get_event_loop().call_later( 0.1, publish ) await ws.send( BxJsonRpcRequest( "2", RpcRequestType.SUBSCRIBE, ["foo", {}] ).to_jsons() ) response = await ws.recv() parsed_response = JsonRpcResponse.from_jsons(response) self.assertEqual("2", parsed_response.id) self.assertIsNotNone("1", parsed_response.result) subscriber_id = parsed_response.result self._assert_notification(1, subscriber_id, await ws.recv()) self._assert_notification(2, subscriber_id, await ws.recv()) self._assert_notification(3, subscriber_id, await ws.recv()) await ws.send( BxJsonRpcRequest( "3", RpcRequestType.UNSUBSCRIBE, [subscriber_id] ).to_jsons() ) response = await ws.recv() parsed_response = JsonRpcResponse.from_jsons(response) self.assertEqual("3", parsed_response.id) self.assertTrue(parsed_response.result) publish() with self.assertRaises(TimeoutError): await asyncio.wait_for(ws.recv(), 0.1)
async def attach_to_container(browser_socket): """ This route is a websocket transceiver/proxy. It receives data from a websocket connected to the browser and transmits it to a websocket connected to the container (via Docker's API). Likewise, data received from the container is transmitted to the browser. The browser_socket is a starlette.websockets.WebSocket object. The container_socket is a websockets.client.WebSocketClientProtocol object. It'd be nice if they had the same API, but alas, they don't. """ # XXX TODO: security: validate Origin is in allowed list before adding any # auth or user state. # Figure out the Docker API ws[s]:// URL to connect to id = browser_socket.path_params["id"] query = browser_socket.url.query attach_url = urlunsplit( ({ "http": "ws", "https": "wss", "http+unix": "ws" }[DOCKER_HOST.scheme], DOCKER_HOST.netloc or "localhost", f"/v1.40/containers/{id}/attach/ws", query, None)) if DOCKER_HOST.scheme == "http+unix": container_connection = websockets.unix_connect(DOCKER_HOST.path, attach_url) else: container_connection = websockets.connect(attach_url) # Connect to the Docker API websocket endpoint try: log("container.attaching", container=id, url=attach_url) container_socket = await container_connection except websockets.exceptions.WebSocketException as error: log("container.attach-failed", container=id, error=error) return await browser_socket.close() try: await browser_socket.accept() # Coroutines to ferry data between the two sockets. async def browser_to_container(): while True: message = await browser_socket.receive() # receive() updates client_state immediately based on the # message, so it's safe to check it here. if browser_socket.client_state != WebSocketState.CONNECTED: break if "bytes" in message: await container_socket.send(message["bytes"]) elif "text" in message: await container_socket.send(message["text"]) else: raise RuntimeError( f"expected a 'bytes' or 'text' key in message: {message!r}" ) async def container_to_browser(): async for data in container_socket: if isinstance(data, bytes): await browser_socket.send_bytes(data) elif isinstance(data, str): await browser_socket.send_text(data) else: raise RuntimeError( f"expected a message of type bytes or str but got {type(data).__name__!r}" ) # Run coroutines concurrently until one of them exits. tasks = [ asyncio.create_task(browser_to_container()), asyncio.create_task(container_to_browser()) ] done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED) for task in pending: task.cancel() finally: await container_socket.close() await browser_socket.close()
async def request(self, req: BxJsonRpcRequest) -> JsonRpcResponse: async with websockets.unix_connect(self.ipc_path) as ws: await ws.send(req.to_jsons()) return JsonRpcResponse.from_jsons(await ws.recv())