diff --git a/.changeset/failover-retry-keeps-base-path.md b/.changeset/failover-retry-keeps-base-path.md new file mode 100644 index 000000000..7dcda8aec --- /dev/null +++ b/.changeset/failover-retry-keeps-base-path.md @@ -0,0 +1,5 @@ +--- +"livekit-api": patch +--- + +Keep the base path of the server URL when a failed request is retried against the same host. diff --git a/livekit-api/livekit/api/twirp_client.py b/livekit-api/livekit/api/twirp_client.py index 4fdb27357..d69d7c3aa 100644 --- a/livekit-api/livekit/api/twirp_client.py +++ b/livekit-api/livekit/api/twirp_client.py @@ -305,8 +305,9 @@ async def request( ) await asyncio.sleep(self._failover_backoff * (2**attempt)) attempted.add(host_key(next_origin)) - current_origin = next_origin - current_base = next_origin + if next_origin != current_origin: + current_origin = next_origin + current_base = next_origin raise RuntimeError("failover loop exited without returning") # unreachable diff --git a/tests/api/test_failover_unit.py b/tests/api/test_failover_unit.py index 898e7cf07..61e591209 100644 --- a/tests/api/test_failover_unit.py +++ b/tests/api/test_failover_unit.py @@ -57,6 +57,7 @@ async def _call_single_host( attempts: List[int], *, host: str = "127.0.0.1", + base_path: str = "", discovery_hits: Optional[List[None]] = None, ) -> Room: """Runs one CreateRoom against a server, reached as ``host``, whose only @@ -81,14 +82,14 @@ async def twirp(request: web.Request) -> web.StreamResponse: return resp app = web.Application() - app.router.add_post("/twirp/livekit.RoomService/CreateRoom", twirp) + app.router.add_post(f"{base_path}/twirp/livekit.RoomService/CreateRoom", twirp) app.router.add_get("/settings/regions", regions) async with TestServer(app) as server: connector = aiohttp.TCPConnector(resolver=_StaticResolver()) async with aiohttp.ClientSession(connector=connector) as session: client = TwirpClient( session, - f"http://{host}:{server.port}", + f"http://{host}:{server.port}{base_path}", "livekit", _failover_force=host == "127.0.0.1", _failover_backoff=0.001, @@ -142,6 +143,20 @@ def behave(attempt: int, request: web.Request): assert len(attempts) == 2 +def test_same_host_retry_keeps_the_base_path(): + """A retry against the same host keeps the base path of the configured URL.""" + + def behave(attempt: int, request: web.Request): + return None if attempt == 0 else _ok(request) + + attempts: List[int] = [] + room = asyncio.run( + _call_single_host(behave, attempts, host="cloud-api.livekit.io", base_path="/livekit") + ) + assert room.name == "r" + assert len(attempts) == 2 + + @pytest.mark.parametrize( "host, expected", [