Example #1
0
    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())
Example #2
0
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}")
Example #3
0
    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)
Example #4
0
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()
Example #5
0
 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())