mirror of
https://github.com/vector-im/element-call.git
synced 2026-08-05 19:59:20 +00:00
Update makeTransport to fetch backend transports and validate all transports before response.
This commit is contained in:
@@ -32,6 +32,7 @@ describe("LocalTransport", () => {
|
|||||||
memberships$: constant(new Epoch<CallMembership[]>([])),
|
memberships$: constant(new Epoch<CallMembership[]>([])),
|
||||||
client: {
|
client: {
|
||||||
getDomain: () => "",
|
getDomain: () => "",
|
||||||
|
_unstable_getRTCTransports: async () => [],
|
||||||
// These won't be called in this error path but satisfy the type
|
// These won't be called in this error path but satisfy the type
|
||||||
getOpenIdToken: vi.fn(),
|
getOpenIdToken: vi.fn(),
|
||||||
getDeviceId: vi.fn(),
|
getDeviceId: vi.fn(),
|
||||||
@@ -67,6 +68,7 @@ describe("LocalTransport", () => {
|
|||||||
client: {
|
client: {
|
||||||
// Use empty domain to skip .well-known and use config directly
|
// Use empty domain to skip .well-known and use config directly
|
||||||
getDomain: () => "",
|
getDomain: () => "",
|
||||||
|
_unstable_getRTCTransports: async () => [],
|
||||||
getOpenIdToken: vi.fn(),
|
getOpenIdToken: vi.fn(),
|
||||||
getDeviceId: vi.fn(),
|
getDeviceId: vi.fn(),
|
||||||
},
|
},
|
||||||
@@ -103,6 +105,7 @@ describe("LocalTransport", () => {
|
|||||||
memberships$: constant(new Epoch<CallMembership[]>([])),
|
memberships$: constant(new Epoch<CallMembership[]>([])),
|
||||||
client: {
|
client: {
|
||||||
getDomain: () => "",
|
getDomain: () => "",
|
||||||
|
_unstable_getRTCTransports: async () => [],
|
||||||
getOpenIdToken: vi.fn(),
|
getOpenIdToken: vi.fn(),
|
||||||
getDeviceId: vi.fn(),
|
getDeviceId: vi.fn(),
|
||||||
},
|
},
|
||||||
@@ -138,6 +141,7 @@ describe("LocalTransport", () => {
|
|||||||
memberships$,
|
memberships$,
|
||||||
client: {
|
client: {
|
||||||
getDomain: () => "",
|
getDomain: () => "",
|
||||||
|
_unstable_getRTCTransports: async () => [],
|
||||||
getOpenIdToken: vi.fn(),
|
getOpenIdToken: vi.fn(),
|
||||||
getDeviceId: vi.fn(),
|
getDeviceId: vi.fn(),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ Please see LICENSE in the repository root for full details.
|
|||||||
import {
|
import {
|
||||||
type CallMembership,
|
type CallMembership,
|
||||||
isLivekitTransport,
|
isLivekitTransport,
|
||||||
type LivekitTransportConfig,
|
|
||||||
type LivekitTransport,
|
type LivekitTransport,
|
||||||
isLivekitTransportConfig,
|
isLivekitTransportConfig,
|
||||||
|
type Transport,
|
||||||
} from "matrix-js-sdk/lib/matrixrtc";
|
} from "matrix-js-sdk/lib/matrixrtc";
|
||||||
import { type MatrixClient } from "matrix-js-sdk";
|
import { MatrixError, type MatrixClient } from "matrix-js-sdk";
|
||||||
import {
|
import {
|
||||||
combineLatest,
|
combineLatest,
|
||||||
distinctUntilChanged,
|
distinctUntilChanged,
|
||||||
@@ -45,7 +45,8 @@ const logger = rootLogger.getChild("[LocalTransport]");
|
|||||||
interface Props {
|
interface Props {
|
||||||
scope: ObservableScope;
|
scope: ObservableScope;
|
||||||
memberships$: Behavior<Epoch<CallMembership[]>>;
|
memberships$: Behavior<Epoch<CallMembership[]>>;
|
||||||
client: Pick<MatrixClient, "getDomain"> & OpenIDClientParts;
|
client: Pick<MatrixClient, "getDomain" | "_unstable_getRTCTransports"> &
|
||||||
|
OpenIDClientParts;
|
||||||
roomId: string;
|
roomId: string;
|
||||||
useOldestMember$: Behavior<boolean>;
|
useOldestMember$: Behavior<boolean>;
|
||||||
}
|
}
|
||||||
@@ -116,73 +117,125 @@ export const createLocalTransport$ = ({
|
|||||||
const FOCI_WK_KEY = "org.matrix.msc4143.rtc_foci";
|
const FOCI_WK_KEY = "org.matrix.msc4143.rtc_foci";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Determine the correct Transport for the current session, including
|
||||||
|
* validating auth against the service to ensure it's correct.
|
||||||
|
* Prefers in order:
|
||||||
*
|
*
|
||||||
* @param client
|
* 1. The transports returned via the homeserver.
|
||||||
* @param roomId
|
* 2. The transports returned via .well-known.
|
||||||
* @returns
|
* 3. The transport configured in Element Call's config.
|
||||||
|
*
|
||||||
|
* @param client The authenticated Matrix client for the current user
|
||||||
|
* @param roomId The ID of the room to be connected to.
|
||||||
|
* @param urlFromDevSettings Override URL provided by the user's local config.
|
||||||
|
* @returns A fully validated transport config.
|
||||||
* @throws MatrixRTCTransportMissingError | FailToGetOpenIdToken
|
* @throws MatrixRTCTransportMissingError | FailToGetOpenIdToken
|
||||||
*/
|
*/
|
||||||
async function makeTransport(
|
async function makeTransport(
|
||||||
client: Pick<MatrixClient, "getDomain"> & OpenIDClientParts,
|
client: Pick<MatrixClient, "getDomain" | "_unstable_getRTCTransports"> &
|
||||||
|
OpenIDClientParts,
|
||||||
roomId: string,
|
roomId: string,
|
||||||
urlFromDevSettings: string | null,
|
urlFromDevSettings: string | null,
|
||||||
): Promise<LivekitTransport> {
|
): Promise<LivekitTransport> {
|
||||||
let transport: LivekitTransport | undefined;
|
|
||||||
logger.trace("Searching for a preferred transport");
|
logger.trace("Searching for a preferred transport");
|
||||||
//TODO refactor this to use the jwt service returned alias.
|
|
||||||
const livekitAlias = roomId;
|
const livekitAlias = roomId;
|
||||||
|
|
||||||
// DEVTOOL: Highest priority: Load from devtool setting
|
// DEVTOOL: Highest priority: Load from devtool setting
|
||||||
if (urlFromDevSettings !== null) {
|
if (urlFromDevSettings !== null) {
|
||||||
const transportFromStorage: LivekitTransport = {
|
logger.info("Using LiveKit transport from dev tools: ", urlFromDevSettings);
|
||||||
|
// Validate that the SFU is up. Otherwise, we want to fail on this
|
||||||
|
// as we don't permit other SFUs.
|
||||||
|
await getSFUConfigWithOpenID(client, urlFromDevSettings, livekitAlias);
|
||||||
|
return {
|
||||||
type: "livekit",
|
type: "livekit",
|
||||||
livekit_service_url: urlFromDevSettings,
|
livekit_service_url: urlFromDevSettings,
|
||||||
livekit_alias: livekitAlias,
|
livekit_alias: livekitAlias,
|
||||||
};
|
};
|
||||||
logger.info(
|
|
||||||
"Using LiveKit transport from dev tools: ",
|
|
||||||
transportFromStorage,
|
|
||||||
);
|
|
||||||
transport = transportFromStorage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WELL_KNOWN: Prioritize the .well-known/matrix/client, if available, over the configured SFU
|
async function getFirstUsableTransport(
|
||||||
|
transports: Transport[],
|
||||||
|
): Promise<LivekitTransport | null> {
|
||||||
|
for (const potentialTransport of transports) {
|
||||||
|
if (isLivekitTransportConfig(potentialTransport)) {
|
||||||
|
try {
|
||||||
|
await getSFUConfigWithOpenID(
|
||||||
|
client,
|
||||||
|
potentialTransport.livekit_service_url,
|
||||||
|
livekitAlias,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
...potentialTransport,
|
||||||
|
livekit_alias: livekitAlias,
|
||||||
|
};
|
||||||
|
} catch (ex) {
|
||||||
|
logger.debug(
|
||||||
|
`Could not use SFU service "${potentialTransport.livekit_service_url}" as SFU`,
|
||||||
|
ex,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MSC4143: Attempt to fetch transports from backend.
|
||||||
|
if ("_unstable_getRTCTransports" in client) {
|
||||||
|
try {
|
||||||
|
const selectedTransport = await getFirstUsableTransport(
|
||||||
|
await client._unstable_getRTCTransports(),
|
||||||
|
);
|
||||||
|
if (selectedTransport) {
|
||||||
|
logger.info("Using backend-configured SFU", selectedTransport);
|
||||||
|
return selectedTransport;
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
if (ex instanceof MatrixError && ex.httpStatus === 404) {
|
||||||
|
// Expected, this is an unstable endpoint and it's not required.
|
||||||
|
logger.debug("Backend does not provide any RTC transports", ex);
|
||||||
|
} else {
|
||||||
|
// We got an error that wasn't just missing support for the feature, so log it loudly.
|
||||||
|
logger.error(
|
||||||
|
"Unexpected error fetching RTC transports from backend",
|
||||||
|
ex,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy MSC4143 (to be removed) WELL_KNOWN: Prioritize the .well-known/matrix/client, if available.
|
||||||
const domain = client.getDomain();
|
const domain = client.getDomain();
|
||||||
if (domain && transport === undefined) {
|
if (domain) {
|
||||||
// we use AutoDiscovery instead of relying on the MatrixClient having already
|
// we use AutoDiscovery instead of relying on the MatrixClient having already
|
||||||
// been fully configured and started
|
// been fully configured and started
|
||||||
const wellKnownFoci = (await AutoDiscovery.getRawClientConfig(domain))?.[
|
const wellKnownFoci = (await AutoDiscovery.getRawClientConfig(domain))?.[
|
||||||
FOCI_WK_KEY
|
FOCI_WK_KEY
|
||||||
];
|
];
|
||||||
if (Array.isArray(wellKnownFoci)) {
|
const selectedTransport = Array.isArray(wellKnownFoci)
|
||||||
const wellKnownTransport: LivekitTransportConfig | undefined =
|
? await getFirstUsableTransport(wellKnownFoci)
|
||||||
wellKnownFoci.find((f) => f && isLivekitTransportConfig(f));
|
: null;
|
||||||
if (wellKnownTransport !== undefined) {
|
if (selectedTransport) {
|
||||||
logger.info("Using LiveKit transport from .well-known: ", transport);
|
logger.info("Using .well-known SFU", selectedTransport);
|
||||||
transport = { ...wellKnownTransport, livekit_alias: livekitAlias };
|
return selectedTransport;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CONFIG: Least prioritized; Load from config file
|
// CONFIG: Least prioritized; Load from config file
|
||||||
const urlFromConf = Config.get().livekit?.livekit_service_url;
|
const urlFromConf = Config.get().livekit?.livekit_service_url;
|
||||||
if (urlFromConf && transport === undefined) {
|
if (urlFromConf) {
|
||||||
const transportFromConf: LivekitTransport = {
|
try {
|
||||||
type: "livekit",
|
await getSFUConfigWithOpenID(client, urlFromConf, roomId);
|
||||||
livekit_service_url: urlFromConf,
|
const selectedTransport: LivekitTransport = {
|
||||||
livekit_alias: livekitAlias,
|
type: "livekit",
|
||||||
};
|
livekit_service_url: urlFromConf,
|
||||||
logger.info("Using LiveKit transport from config: ", transportFromConf);
|
livekit_alias: livekitAlias,
|
||||||
transport = transportFromConf;
|
};
|
||||||
|
logger.info("Using config SFU", selectedTransport);
|
||||||
|
return selectedTransport;
|
||||||
|
} catch (ex) {
|
||||||
|
logger.error("Failed to validate config SFU", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!transport) throw new MatrixRTCTransportMissingError(domain ?? ""); // this will call the jwt/sfu/get endpoint to pre create the livekit room.
|
throw new MatrixRTCTransportMissingError(domain ?? "");
|
||||||
|
|
||||||
await getSFUConfigWithOpenID(
|
|
||||||
client,
|
|
||||||
transport.livekit_service_url,
|
|
||||||
transport.livekit_alias,
|
|
||||||
);
|
|
||||||
|
|
||||||
return transport;
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user