bridge (dbus): Add time out on connection:

- Add a 5s timeout on connection to avoid infinite hang when socket is not working.
- Raise `BridgeInitError` on time-out (needed to make `auto` bridge fallback correctly).
This commit is contained in:
Goffi 2026-10-30 22:19:59 +01:00
commit c6ea6ea27b
4 changed files with 100 additions and 8 deletions

View file

@ -36,6 +36,7 @@ const_ERROR_PREFIX = const_INT_PREFIX + ".error"
const_OBJ_PATH = "/org/libervia/Libervia/bridge"
const_CORE_SUFFIX = ".core"
const_PLUGIN_SUFFIX = ".plugin"
const_CONNECT_TIMEOUT = 5
class ParseError(Exception):
@ -123,7 +124,23 @@ class bridge:
async def post_init(self):
try:
self.conn = await client.connect(reactor)
d = client.connect(reactor)
reactor.callLater(const_CONNECT_TIMEOUT, d.cancel)
def on_cancel(failure):
if failure.check(defer.CancelledError):
log.error(
f"D-Bus connection timed out after {const_CONNECT_TIMEOUT} s, "
"please check the D-Bus service"
)
return failure
d.addErrback(on_cancel)
self.conn = await d
except defer.CancelledError:
# The connect deferred was cancelled by the timeout, we raise BridgeInitError
# so that the "auto" bridge can fall back to the next one.
raise BridgeInitError from None
except error.DBusException as e:
if e.errName == "org.freedesktop.DBus.Error.NotSupported":
log.error(

View file

@ -19,7 +19,7 @@
import asyncio
from functools import partial
from twisted.internet import reactor
from twisted.internet import defer, reactor
from txdbus import client
from .bridge_frontend import BridgeException
@ -40,6 +40,27 @@ const_OBJ_PATH = "/org/libervia/Libervia/bridge"
const_CORE_SUFFIX = ".core"
const_PLUGIN_SUFFIX = ".plugin"
const_TIMEOUT = 120
const_CONNECT_TIMEOUT = 5
def _connect_dbus():
"""Connect to the D-Bus session bus with a timeout.
@return: Deferred to the D-Bus connection
"""
d = client.connect(reactor)
reactor.callLater(const_CONNECT_TIMEOUT, d.cancel)
def on_cancel(failure):
if failure.check(defer.CancelledError):
log.error(
f"D-Bus connection timed out after {const_CONNECT_TIMEOUT} s, "
"please check the D-Bus service"
)
return failure
d.addErrback(on_cancel)
return d
def dbus_to_bridge_exception(failure):
@ -192,7 +213,7 @@ class bridge:
async def bridge_connect(self) -> None:
"""Connect to the D-Bus bridge."""
try:
self.conn = await client.connect(reactor)
self.conn = await _connect_dbus()
self.db_object = await self.conn.getRemoteObject(
const_INT_PREFIX, const_OBJ_PATH
)
@ -218,6 +239,10 @@ class bridge:
)
except BridgeInitError as e:
raise e
except defer.CancelledError:
# The connect deferred was cancelled by the timeout, we raise BridgeInitError
# so that the "auto" bridge can fall back to the next one.
raise BridgeInitError from None
except Exception as e:
msg = str(e)
if (
@ -281,7 +306,7 @@ class AIOBridge(bridge):
async def bridge_connect(self):
"""Connect to the D-Bus bridge (async version)."""
try:
self.conn = await client.connect(reactor).asFuture(asyncio.get_event_loop())
self.conn = await _connect_dbus().asFuture(asyncio.get_event_loop())
self.db_object = await self.conn.getRemoteObject(
const_INT_PREFIX, const_OBJ_PATH
).asFuture(asyncio.get_event_loop())
@ -308,6 +333,10 @@ class AIOBridge(bridge):
)
except BridgeInitError as e:
raise e
except defer.CancelledError:
# The connect deferred was cancelled by the timeout, we raise BridgeInitError
# so that the "auto" bridge can fall back to the next one.
raise BridgeInitError from None
except Exception as e:
msg = str(e)
if (

View file

@ -36,6 +36,7 @@ const_ERROR_PREFIX = const_INT_PREFIX + ".error"
const_OBJ_PATH = "/org/libervia/Libervia/bridge"
const_CORE_SUFFIX = ".core"
const_PLUGIN_SUFFIX = ".plugin"
const_CONNECT_TIMEOUT = 5
class ParseError(Exception):
@ -571,7 +572,23 @@ class bridge:
async def post_init(self):
try:
self.conn = await client.connect(reactor)
d = client.connect(reactor)
reactor.callLater(const_CONNECT_TIMEOUT, d.cancel)
def on_cancel(failure):
if failure.check(defer.CancelledError):
log.error(
f"D-Bus connection timed out after {const_CONNECT_TIMEOUT} s, "
"please check the D-Bus service"
)
return failure
d.addErrback(on_cancel)
self.conn = await d
except defer.CancelledError:
# The connect deferred was cancelled by the timeout, we raise BridgeInitError
# so that the "auto" bridge can fall back to the next one.
raise BridgeInitError from None
except error.DBusException as e:
if e.errName == "org.freedesktop.DBus.Error.NotSupported":
log.error(

View file

@ -19,7 +19,7 @@
import asyncio
from functools import partial
from twisted.internet import reactor
from twisted.internet import defer, reactor
from txdbus import client
from .bridge_frontend import BridgeException
@ -40,6 +40,27 @@ const_OBJ_PATH = "/org/libervia/Libervia/bridge"
const_CORE_SUFFIX = ".core"
const_PLUGIN_SUFFIX = ".plugin"
const_TIMEOUT = 120
const_CONNECT_TIMEOUT = 5
def _connect_dbus():
"""Connect to the D-Bus session bus with a timeout.
@return: Deferred to the D-Bus connection
"""
d = client.connect(reactor)
reactor.callLater(const_CONNECT_TIMEOUT, d.cancel)
def on_cancel(failure):
if failure.check(defer.CancelledError):
log.error(
f"D-Bus connection timed out after {const_CONNECT_TIMEOUT} s, "
"please check the D-Bus service"
)
return failure
d.addErrback(on_cancel)
return d
def dbus_to_bridge_exception(failure):
@ -192,7 +213,7 @@ class bridge:
async def bridge_connect(self) -> None:
"""Connect to the D-Bus bridge."""
try:
self.conn = await client.connect(reactor)
self.conn = await _connect_dbus()
self.db_object = await self.conn.getRemoteObject(
const_INT_PREFIX, const_OBJ_PATH
)
@ -218,6 +239,10 @@ class bridge:
)
except BridgeInitError as e:
raise e
except defer.CancelledError:
# The connect deferred was cancelled by the timeout, we raise BridgeInitError
# so that the "auto" bridge can fall back to the next one.
raise BridgeInitError from None
except Exception as e:
msg = str(e)
if (
@ -963,7 +988,7 @@ class AIOBridge(bridge):
async def bridge_connect(self):
"""Connect to the D-Bus bridge (async version)."""
try:
self.conn = await client.connect(reactor).asFuture(asyncio.get_event_loop())
self.conn = await _connect_dbus().asFuture(asyncio.get_event_loop())
self.db_object = await self.conn.getRemoteObject(
const_INT_PREFIX, const_OBJ_PATH
).asFuture(asyncio.get_event_loop())
@ -990,6 +1015,10 @@ class AIOBridge(bridge):
)
except BridgeInitError as e:
raise e
except defer.CancelledError:
# The connect deferred was cancelled by the timeout, we raise BridgeInitError
# so that the "auto" bridge can fall back to the next one.
raise BridgeInitError from None
except Exception as e:
msg = str(e)
if (