xrpl-py icon indicating copy to clipboard operation
xrpl-py copied to clipboard

Async Client Doesn't Handle Internet Disconnection

Open Amindv1 opened this issue 1 year ago • 4 comments

Description

It seems like the xrpl.py async client doesn't handle wifi disconnections:

    def is_open(self: WebsocketBase) -> bool:
        """
        Returns whether the client is currently open.

        Returns:
            True if the client is currently open, False otherwise.
        """
        return (
            self._handler_task is not None
            and self._messages is not None
            and self._websocket is not None
            and self._websocket.open
        )

This is the logic it uses to see if a connection is open. Please correct me if im wrong: At first glance it looks correct since it checks self._websocket.open but since we're waiting for data from our peer, the TCP network can't tell the difference between a healthy and a broken connection when the peer isn't sending us any data. So this field never updates and the websocket doesn't close - and in turn I can't attempt to reconnect.

Oddly enough even after reconnecting the wifi the websockets don't reconnect (They only reconnect if I disconnect and reconnect the wifi quickly ~2s).

Reproduction

You can just paste the following code in a python file and run it (you need the imports). After youre seeing logs disconnect the wifi and wait about a minute, then reconnect.

If you don't wait around a minute it'll reconnect on its own, but if you wait for longer it doesnt handle reconnection.

from aiohttp import web, web_app
import logging

from websockets.exceptions import ConnectionClosedError
from xrpl.asyncio.clients import (
    AsyncWebsocketClient,
)
from xrpl.models import StreamParameter, Subscribe

logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)


async def start(app: web_app.Application):
    stream_type = StreamParameter.LEDGER
    ledger_update_sub_req = Subscribe(
        streams = [stream_type]
    )

    async with AsyncWebsocketClient("wss://s1.ripple.com") as client:
        # one time subscription
        logger.info("[LedgerCreationDataSource] Sending subscribe request")
        await client.send(ledger_update_sub_req)

        try:
            async for message in client:
                logger.info("[LedgerCreationDataSource] received message: " + str(message))
        except ConnectionClosedError:
            logger.error("[LedgerCreationDataSource] Connection closed - connection closed error")

    logger.warning("[LedgerCreationDataSource] Connection closed - server kicked us off")

if __name__ == "__main__":
    app = web.Application()
    app.on_startup.append(start)

    web.run_app(app)

Amindv1 avatar Apr 11 '23 21:04 Amindv1