centrifugal / centrifuge-python

Centrifugo real-time WebSocket SDK for Python on top of asyncio
https://centrifugal.dev
MIT License
30 stars 14 forks source link

Passing sock parameter to websockets library #26

Open mehrant95 opened 4 weeks ago

mehrant95 commented 4 weeks ago

Hi,

I'm using this library on a server with multiple public IP addresses and I need to be able to specify an exact IP address for websockets library to use. I checked library codes and found where it calls the connect function of websockets library:

async def _create_connection(self) -> bool:
      if self.state != ClientState.CONNECTING:
          return False
      subprotocols = []
      if self._use_protobuf:
          subprotocols = ["centrifuge-protobuf"]
      try:
          self._conn = await websockets.connect(
              self._address,
              subprotocols=subprotocols,
              extra_headers=self._headers,
          )
      except (OSError, exceptions.WebSocketException) as e:
          handler = self.events.on_error
          await handler(ErrorContext(code=_code_number(_ErrorCode.TRANSPORT_CLOSED), error=e))
          asyncio.ensure_future(self._schedule_reconnect())
          return False

To specify a certain IP address for websockets library, I need to pass sock parameter to the websockets.connect function, like this:

async def _create_connection(self) -> bool:
      if self.state != ClientState.CONNECTING:
          return False
      subprotocols = []
      if self._use_protobuf:
          subprotocols = ["centrifuge-protobuf"]
      try:
          self._conn = await websockets.connect(
              self._address,
              subprotocols=subprotocols,
              extra_headers=self._headers,
              sock=my_tcp_socket
          )
      except (OSError, exceptions.WebSocketException) as e:
          handler = self.events.on_error
          await handler(ErrorContext(code=_code_number(_ErrorCode.TRANSPORT_CLOSED), error=e))
          asyncio.ensure_future(self._schedule_reconnect())
          return False

So if it's possible, please add a sock argument to Client class and then pass it to websockets.connect function in _create_connection function.

Thanks for developing this library.

FZambia commented 4 weeks ago

Hello @mehrant95

Client manages reconnects automatically, I guess socket object can't be re-used upon reconnections and should be allocated from scratch each time? Did you look at it? Probably this can only be solved by a custom callback function which will be called before each connection attempt.

mehrant95 commented 4 weeks ago

@FZambia You're right, I didn't consider the reconnection mechanism.

I've tested library for many hours and sometimes websocket connection is lost due to network errors or server-side problems, but library gets stuck at reconnecting. I tried to figure out why that happened by reading debug logs but no luck, so I had no other option but to set _need_reconnect attribute to False and manage reconnection by myself (typically by stopping current tasks, instantiating a new Client object and so on). Perhaps that's why I didn't notice the problem you just described.

So I think other people who rely on the library internal reconnection mechanism, should re-initialize the socket object in on_connecting callback.