Merge pull request #3444 from element-hq/valere/force_jwt_service_before_start
Ensure that the jwt service is called before starting a call
This commit is contained in:
75
playwright/restricted-sfu.spec.ts
Normal file
75
playwright/restricted-sfu.spec.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2025 New Vector Ltd.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
Please see LICENSE in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { expect, test } from "@playwright/test";
|
||||||
|
import { sleep } from "matrix-js-sdk/lib/utils.js";
|
||||||
|
|
||||||
|
test("Should request JWT token before starting the call", async ({ page }) => {
|
||||||
|
await page.goto("/");
|
||||||
|
|
||||||
|
let sfGetTimestamp = 0;
|
||||||
|
let sendStateEventTimestamp = 0;
|
||||||
|
await page.route(
|
||||||
|
"**/matrix-rtc.m.localhost/livekit/jwt/sfu/get",
|
||||||
|
async (route) => {
|
||||||
|
await sleep(2000); // Simulate very slow request
|
||||||
|
await route.continue();
|
||||||
|
sfGetTimestamp = Date.now();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.route(
|
||||||
|
"**/state/org.matrix.msc3401.call.member/**",
|
||||||
|
async (route) => {
|
||||||
|
await route.continue();
|
||||||
|
sendStateEventTimestamp = Date.now();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.getByTestId("home_callName").click();
|
||||||
|
await page.getByTestId("home_callName").fill("HelloCall");
|
||||||
|
await page.getByTestId("home_displayName").click();
|
||||||
|
await page.getByTestId("home_displayName").fill("John Doe");
|
||||||
|
await page.getByTestId("home_go").click();
|
||||||
|
|
||||||
|
// Join the call
|
||||||
|
await page.getByTestId("lobby_joinCall").click();
|
||||||
|
await page.waitForTimeout(4000);
|
||||||
|
// Ensure that the call is connected
|
||||||
|
await page
|
||||||
|
.locator("div")
|
||||||
|
.filter({ hasText: /^HelloCall$/ })
|
||||||
|
.click();
|
||||||
|
|
||||||
|
expect(sfGetTimestamp).toBeGreaterThan(0);
|
||||||
|
expect(sendStateEventTimestamp).toBeGreaterThan(0);
|
||||||
|
expect(sfGetTimestamp).toBeLessThan(sendStateEventTimestamp);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Error when pre-warming the focus are caught by the ErrorBoundary", async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
await page.goto("/");
|
||||||
|
|
||||||
|
await page.route("**/openid/request_token", async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 418, // Simulate an error not retryable
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.getByTestId("home_callName").click();
|
||||||
|
await page.getByTestId("home_callName").fill("HelloCall");
|
||||||
|
await page.getByTestId("home_displayName").click();
|
||||||
|
await page.getByTestId("home_displayName").fill("John Doe");
|
||||||
|
await page.getByTestId("home_go").click();
|
||||||
|
|
||||||
|
// Join the call
|
||||||
|
await page.getByTestId("lobby_joinCall").click();
|
||||||
|
|
||||||
|
// Should fail
|
||||||
|
await expect(page.getByText("Something went wrong")).toBeVisible();
|
||||||
|
});
|
||||||
@@ -100,5 +100,5 @@ test("When creator left, avoid reconnect to the same SFU", async ({
|
|||||||
// https://github.com/element-hq/element-call/issues/3344
|
// https://github.com/element-hq/element-call/issues/3344
|
||||||
// The app used to request a new jwt token then to reconnect to the SFU
|
// The app used to request a new jwt token then to reconnect to the SFU
|
||||||
expect(wsConnectionCount).toBe(1);
|
expect(wsConnectionCount).toBe(1);
|
||||||
expect(sfuGetCallCount).toBe(1);
|
expect(sfuGetCallCount).toBe(2 /* the first one is for the warmup */);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -70,6 +70,12 @@ test("It joins the correct Session", async () => {
|
|||||||
roomId: "roomId",
|
roomId: "roomId",
|
||||||
client: {
|
client: {
|
||||||
getDomain: vi.fn().mockReturnValue("example.org"),
|
getDomain: vi.fn().mockReturnValue("example.org"),
|
||||||
|
getOpenIdToken: vi.fn().mockResolvedValue({
|
||||||
|
access_token: "ACCCESS_TOKEN",
|
||||||
|
token_type: "Bearer",
|
||||||
|
matrix_server_name: "localhost",
|
||||||
|
expires_in: 10000,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
memberships: [],
|
memberships: [],
|
||||||
@@ -195,6 +201,12 @@ test("It should not fail with configuration error if homeserver config has livek
|
|||||||
roomId: "roomId",
|
roomId: "roomId",
|
||||||
client: {
|
client: {
|
||||||
getDomain: vi.fn().mockReturnValue("example.org"),
|
getDomain: vi.fn().mockReturnValue("example.org"),
|
||||||
|
getOpenIdToken: vi.fn().mockResolvedValue({
|
||||||
|
access_token: "ACCCESS_TOKEN",
|
||||||
|
token_type: "Bearer",
|
||||||
|
matrix_server_name: "localhost",
|
||||||
|
expires_in: 10000,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
memberships: [],
|
memberships: [],
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|||||||
Please see LICENSE in the repository root for full details.
|
Please see LICENSE in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { type MatrixRTCSession } from "matrix-js-sdk/lib/matrixrtc";
|
|
||||||
import { logger } from "matrix-js-sdk/lib/logger";
|
|
||||||
import {
|
import {
|
||||||
isLivekitFocus,
|
isLivekitFocus,
|
||||||
isLivekitFocusConfig,
|
isLivekitFocusConfig,
|
||||||
type LivekitFocus,
|
type LivekitFocus,
|
||||||
type LivekitFocusActive,
|
type LivekitFocusActive,
|
||||||
|
type MatrixRTCSession,
|
||||||
} from "matrix-js-sdk/lib/matrixrtc";
|
} from "matrix-js-sdk/lib/matrixrtc";
|
||||||
|
import { logger } from "matrix-js-sdk/lib/logger";
|
||||||
import { AutoDiscovery } from "matrix-js-sdk/lib/autodiscovery";
|
import { AutoDiscovery } from "matrix-js-sdk/lib/autodiscovery";
|
||||||
|
|
||||||
import { PosthogAnalytics } from "./analytics/PosthogAnalytics";
|
import { PosthogAnalytics } from "./analytics/PosthogAnalytics";
|
||||||
@@ -20,6 +20,7 @@ import { Config } from "./config/Config";
|
|||||||
import { ElementWidgetActions, widget, type WidgetHelpers } from "./widget";
|
import { ElementWidgetActions, widget, type WidgetHelpers } from "./widget";
|
||||||
import { MatrixRTCFocusMissingError } from "./utils/errors";
|
import { MatrixRTCFocusMissingError } from "./utils/errors";
|
||||||
import { getUrlParams } from "./UrlParams";
|
import { getUrlParams } from "./UrlParams";
|
||||||
|
import { getSFUConfigWithOpenID } from "./livekit/openIDSFU.ts";
|
||||||
|
|
||||||
const FOCI_WK_KEY = "org.matrix.msc4143.rtc_foci";
|
const FOCI_WK_KEY = "org.matrix.msc4143.rtc_foci";
|
||||||
|
|
||||||
@@ -46,6 +47,9 @@ async function makePreferredLivekitFoci(
|
|||||||
preferredFoci.push(focusInUse);
|
preferredFoci.push(focusInUse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Warm up the first focus we owned, to ensure livekit room is created before any state event sent.
|
||||||
|
let toWarmUp: LivekitFocus | undefined;
|
||||||
|
|
||||||
// Prioritize the .well-known/matrix/client, if available, over the configured SFU
|
// Prioritize the .well-known/matrix/client, if available, over the configured SFU
|
||||||
const domain = rtcSession.room.client.getDomain();
|
const domain = rtcSession.room.client.getDomain();
|
||||||
if (domain) {
|
if (domain) {
|
||||||
@@ -55,18 +59,17 @@ async function makePreferredLivekitFoci(
|
|||||||
FOCI_WK_KEY
|
FOCI_WK_KEY
|
||||||
];
|
];
|
||||||
if (Array.isArray(wellKnownFoci)) {
|
if (Array.isArray(wellKnownFoci)) {
|
||||||
preferredFoci.push(
|
const validWellKnownFoci = wellKnownFoci
|
||||||
...wellKnownFoci
|
|
||||||
.filter((f) => !!f)
|
.filter((f) => !!f)
|
||||||
.filter(isLivekitFocusConfig)
|
.filter(isLivekitFocusConfig)
|
||||||
.map((wellKnownFocus) => {
|
.map((wellKnownFocus) => {
|
||||||
logger.log(
|
logger.log("Adding livekit focus from well known: ", wellKnownFocus);
|
||||||
"Adding livekit focus from well known: ",
|
|
||||||
wellKnownFocus,
|
|
||||||
);
|
|
||||||
return { ...wellKnownFocus, livekit_alias: livekitAlias };
|
return { ...wellKnownFocus, livekit_alias: livekitAlias };
|
||||||
}),
|
});
|
||||||
);
|
if (validWellKnownFoci.length > 0) {
|
||||||
|
toWarmUp = validWellKnownFoci[0];
|
||||||
|
}
|
||||||
|
preferredFoci.push(...validWellKnownFoci);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,10 +80,15 @@ async function makePreferredLivekitFoci(
|
|||||||
livekit_service_url: urlFromConf,
|
livekit_service_url: urlFromConf,
|
||||||
livekit_alias: livekitAlias,
|
livekit_alias: livekitAlias,
|
||||||
};
|
};
|
||||||
|
toWarmUp = toWarmUp ?? focusFormConf;
|
||||||
logger.log("Adding livekit focus from config: ", focusFormConf);
|
logger.log("Adding livekit focus from config: ", focusFormConf);
|
||||||
preferredFoci.push(focusFormConf);
|
preferredFoci.push(focusFormConf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (toWarmUp) {
|
||||||
|
// this will call the jwt/sfu/get endpoint to pre create the livekit room.
|
||||||
|
await getSFUConfigWithOpenID(rtcSession.room.client, toWarmUp);
|
||||||
|
}
|
||||||
if (preferredFoci.length === 0)
|
if (preferredFoci.length === 0)
|
||||||
throw new MatrixRTCFocusMissingError(domain ?? "");
|
throw new MatrixRTCFocusMissingError(domain ?? "");
|
||||||
return Promise.resolve(preferredFoci);
|
return Promise.resolve(preferredFoci);
|
||||||
|
|||||||
Reference in New Issue
Block a user