mirror of
https://github.com/vector-im/element-call.git
synced 2026-08-29 21:15:19 +00:00
Compare commits
3 Commits
legacy-mem
...
langleyd/e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3dbef7a552 | ||
|
|
a75eeaeda1 | ||
|
|
23a9e0c85e |
@@ -123,6 +123,26 @@ describe("UrlParams", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("enableClientWellKnownLookups", () => {
|
||||
it("is undefined when not set (so the config value applies)", () => {
|
||||
expect(computeUrlParams().enableClientWellKnownLookups).toBeUndefined();
|
||||
});
|
||||
|
||||
it("is false when set to false", () => {
|
||||
expect(
|
||||
computeUrlParams("?enableClientWellKnownLookups=false")
|
||||
.enableClientWellKnownLookups,
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("is true when set to true", () => {
|
||||
expect(
|
||||
computeUrlParams("?enableClientWellKnownLookups=true")
|
||||
.enableClientWellKnownLookups,
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("returnToLobby", () => {
|
||||
it("is false in SPA mode", () => {
|
||||
expect(computeUrlParams("?returnToLobby=true").returnToLobby).toBe(false);
|
||||
|
||||
@@ -187,6 +187,14 @@ export interface UrlConfiguration {
|
||||
*/
|
||||
allowIceFallback: boolean;
|
||||
|
||||
/**
|
||||
* Whether the app may make client `.well-known` lookups against the user's
|
||||
* homeserver `server_name` (the post-login well-known poll and the legacy
|
||||
* MatrixRTC foci `.well-known` fallback). Set by the embedder in widget mode;
|
||||
* when unset the `enable_client_well_known_lookups` config value applies.
|
||||
*/
|
||||
enableClientWellKnownLookups?: boolean;
|
||||
|
||||
/**
|
||||
* Whether the app should use per participant keys for E2EE.
|
||||
*/
|
||||
@@ -472,6 +480,9 @@ export const computeUrlParams = (search = "", hash = ""): UrlParams => {
|
||||
showControls: parser.getFlag("showControls"),
|
||||
hideScreensharing: parser.getFlag("hideScreensharing"),
|
||||
allowIceFallback: parser.getFlag("allowIceFallback"),
|
||||
enableClientWellKnownLookups: parser.getFlag(
|
||||
"enableClientWellKnownLookups",
|
||||
),
|
||||
perParticipantE2EE: parser.getFlag("perParticipantE2EE"),
|
||||
controlledAudioDevices: parser.getFlag("controlledAudioDevices"),
|
||||
skipLobby: isWidget ? parser.getFlag("skipLobby") : false,
|
||||
|
||||
@@ -90,6 +90,19 @@ export class Config {
|
||||
return Config.get().default_server_config?.["m.homeserver"].server_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the app may make client `.well-known` lookups against the
|
||||
* homeserver `server_name`. The embedded (widget) deployment sets this via
|
||||
* the `enableClientWellKnownLookups` URL parameter; the standalone (SPA)
|
||||
* deployment sets it via `enable_client_well_known_lookups` in config.json.
|
||||
*/
|
||||
public static clientWellKnownLookupsEnabled(): boolean {
|
||||
return (
|
||||
getUrlParams().enableClientWellKnownLookups ??
|
||||
Config.get().enable_client_well_known_lookups
|
||||
);
|
||||
}
|
||||
|
||||
public config?: ResolvedConfigOptions;
|
||||
private initPromise?: Promise<void>;
|
||||
}
|
||||
|
||||
@@ -70,6 +70,16 @@ export interface ConfigOptions {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether the app may make client `.well-known` lookups against the user's
|
||||
* homeserver `server_name` (the post-login well-known poll and the legacy
|
||||
* MatrixRTC foci `.well-known` fallback). Set to false to keep the app on the
|
||||
* homeserver base URL. This is the standalone (SPA) deployment control;
|
||||
* embedded deployments set it via the `enableClientWellKnownLookups` URL
|
||||
* parameter instead. Defaults to true.
|
||||
*/
|
||||
enable_client_well_known_lookups?: boolean;
|
||||
|
||||
// Describes the LiveKit configuration to be used.
|
||||
livekit?: {
|
||||
// The link to the service that returns a livekit url and token to use it.
|
||||
@@ -189,6 +199,7 @@ export interface ResolvedConfigOptions extends ConfigOptions {
|
||||
server_name: string;
|
||||
};
|
||||
};
|
||||
enable_client_well_known_lookups: boolean;
|
||||
sync_disconnect_grace_period_ms: number;
|
||||
ssla: string;
|
||||
matrix_rtc_session: {
|
||||
@@ -211,6 +222,7 @@ export const DEFAULT_CONFIG: ResolvedConfigOptions = {
|
||||
features: {
|
||||
feature_use_device_session_member_events: true,
|
||||
},
|
||||
enable_client_well_known_lookups: true,
|
||||
sync_disconnect_grace_period_ms: 10000,
|
||||
ssla: "https://static.element.io/legal/element-software-and-services-license-agreement-uk-1.pdf",
|
||||
matrix_rtc_session: {
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
type CallMembership,
|
||||
type LivekitTransportConfig,
|
||||
} from "matrix-js-sdk/lib/matrixrtc";
|
||||
import { MatrixError } from "matrix-js-sdk";
|
||||
import { BehaviorSubject, filter, lastValueFrom } from "rxjs";
|
||||
import fetchMock from "fetch-mock";
|
||||
|
||||
@@ -471,6 +472,11 @@ describe("LocalTransport", () => {
|
||||
|
||||
it("supports getting transport via well-known", async () => {
|
||||
localTransportOpts.client.getDomain.mockReturnValue("example.org");
|
||||
// Pretend the server doesn't implement the transports endpoint to trigger
|
||||
// the .well-known fallback.
|
||||
localTransportOpts.client._unstable_getRTCTransports.mockRejectedValue(
|
||||
new MatrixError({ errcode: "M_UNRECOGNIZED" }, 404),
|
||||
);
|
||||
fetchMock.getOnce("https://example.org/.well-known/matrix/client", {
|
||||
"org.matrix.msc4143.rtc_foci": [
|
||||
{ type: "livekit", livekit_service_url: "https://lk.example.org" },
|
||||
@@ -501,6 +507,11 @@ describe("LocalTransport", () => {
|
||||
|
||||
it("fails fast if the openId request fails for the well-known config", async () => {
|
||||
localTransportOpts.client.getDomain.mockReturnValue("example.org");
|
||||
// Pretend the server doesn't implement the transports endpoint to trigger
|
||||
// the .well-known fallback.
|
||||
localTransportOpts.client._unstable_getRTCTransports.mockRejectedValue(
|
||||
new MatrixError({ errcode: "M_UNRECOGNIZED" }, 404),
|
||||
);
|
||||
fetchMock.getOnce("https://example.org/.well-known/matrix/client", {
|
||||
"org.matrix.msc4143.rtc_foci": [
|
||||
{ type: "livekit", livekit_service_url: "https://lk.example.org" },
|
||||
|
||||
@@ -147,6 +147,7 @@ export const createLocalTransport$ = ({
|
||||
const transportDiscovery = new RtcTransportAutoDiscovery({
|
||||
client: client,
|
||||
resolvedConfig: Config.get(),
|
||||
enableClientWellKnownLookups: Config.clientWellKnownLookupsEnabled(),
|
||||
wellKnownFetcher: AutoDiscovery.getRawClientConfig.bind(AutoDiscovery),
|
||||
logger: logger,
|
||||
});
|
||||
|
||||
@@ -38,6 +38,11 @@ const wellKnownTransport: LivekitTransportConfig = {
|
||||
livekit_service_url: "https://well-known.example.org",
|
||||
};
|
||||
|
||||
const configTransport: LivekitTransportConfig = {
|
||||
type: "livekit",
|
||||
livekit_service_url: "https://config.example.org",
|
||||
};
|
||||
|
||||
function makeClient(): MockedObject<DiscoveryClient> {
|
||||
return {
|
||||
getDomain: vi.fn().mockReturnValue("example.org"),
|
||||
@@ -65,10 +70,29 @@ function makeWellKnown(rtcFoci?: Transport[]): IClientWellKnown {
|
||||
} as unknown as IClientWellKnown;
|
||||
}
|
||||
|
||||
// Error returned by a homeserver that does not implement the endpoint. This is
|
||||
// the only failure that permits falling back to the legacy `.well-known` lookup.
|
||||
const notImplementedError = new MatrixError({ errcode: "M_UNRECOGNIZED" }, 404);
|
||||
|
||||
function makeDiscovery(
|
||||
overrides: Partial<RtcTransportAutoDiscoveryProps> & {
|
||||
client: DiscoveryClient;
|
||||
wellKnownFetcher: RtcTransportAutoDiscoveryProps["wellKnownFetcher"];
|
||||
},
|
||||
): RtcTransportAutoDiscovery {
|
||||
return new RtcTransportAutoDiscovery({
|
||||
resolvedConfig: makeResolvedConfig("https://config.example.org"),
|
||||
enableClientWellKnownLookups: true,
|
||||
logger: rootLogger,
|
||||
...overrides,
|
||||
});
|
||||
}
|
||||
|
||||
describe("RtcTransportAutoDiscovery", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
const VALID_TEST_CASES: Array<{ transports: Transport[] }> = [
|
||||
{ transports: [backendTransport] },
|
||||
// will pick the first livekit transport in the list, even if there are other non-livekit transports
|
||||
@@ -77,7 +101,6 @@ describe("RtcTransportAutoDiscovery", () => {
|
||||
it.each(VALID_TEST_CASES)(
|
||||
"prefers backend transport over well-known and app config $transports",
|
||||
async ({ transports }) => {
|
||||
// it("prefers backend transport over well-known and app config", async () => {
|
||||
const client = makeClient();
|
||||
client._unstable_getRTCTransports.mockResolvedValue(transports);
|
||||
|
||||
@@ -85,12 +108,7 @@ describe("RtcTransportAutoDiscovery", () => {
|
||||
.fn<(domain: string) => Promise<IClientWellKnown>>()
|
||||
.mockResolvedValue(makeWellKnown([wellKnownTransport]));
|
||||
|
||||
const discovery = new RtcTransportAutoDiscovery({
|
||||
client,
|
||||
resolvedConfig: makeResolvedConfig("https://config.example.org"),
|
||||
wellKnownFetcher,
|
||||
logger: rootLogger,
|
||||
});
|
||||
const discovery = makeDiscovery({ client, wellKnownFetcher });
|
||||
|
||||
await expect(
|
||||
discovery.discoverPreferredTransport(),
|
||||
@@ -120,12 +138,7 @@ describe("RtcTransportAutoDiscovery", () => {
|
||||
.fn<(domain: string) => Promise<IClientWellKnown>>()
|
||||
.mockResolvedValue(makeWellKnown([wellKnownTransport]));
|
||||
|
||||
const discovery = new RtcTransportAutoDiscovery({
|
||||
client,
|
||||
resolvedConfig: makeResolvedConfig("https://config.example.org"),
|
||||
wellKnownFetcher,
|
||||
logger: rootLogger,
|
||||
});
|
||||
const discovery = makeDiscovery({ client, wellKnownFetcher });
|
||||
|
||||
await expect(discovery.discoverPreferredTransport()).resolves.toStrictEqual(
|
||||
backendTransport,
|
||||
@@ -135,12 +148,15 @@ describe("RtcTransportAutoDiscovery", () => {
|
||||
expect(wellKnownFetcher).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
const INVALID_TEST_CASES: Array<{ transports: Transport[] }> = [
|
||||
// A homeserver that implements the endpoint but returns no usable livekit
|
||||
// transport has answered authoritatively, so we must NOT leak to the legacy
|
||||
// `.well-known` lookup. Instead we fall straight through to app config.
|
||||
const AUTHORITATIVE_EMPTY_CASES: Array<{ transports: Transport[] }> = [
|
||||
{ transports: [] },
|
||||
{ transports: [{ type: "not_livekit" }] },
|
||||
];
|
||||
it.each(INVALID_TEST_CASES)(
|
||||
"falls back to well-known when backend has no (valid) livekit transports $transports",
|
||||
it.each(AUTHORITATIVE_EMPTY_CASES)(
|
||||
"does not fall back to well-known when the backend answers without a livekit transport $transports",
|
||||
async ({ transports }) => {
|
||||
const client = makeClient();
|
||||
client._unstable_getRTCTransports.mockResolvedValue(transports);
|
||||
@@ -149,21 +165,54 @@ describe("RtcTransportAutoDiscovery", () => {
|
||||
.fn<(domain: string) => Promise<IClientWellKnown>>()
|
||||
.mockResolvedValue(makeWellKnown([wellKnownTransport]));
|
||||
|
||||
const discovery = new RtcTransportAutoDiscovery({
|
||||
client,
|
||||
resolvedConfig: makeResolvedConfig("https://config.example.org"),
|
||||
wellKnownFetcher,
|
||||
logger: rootLogger,
|
||||
});
|
||||
const discovery = makeDiscovery({ client, wellKnownFetcher });
|
||||
|
||||
await expect(
|
||||
discovery.discoverPreferredTransport(),
|
||||
).resolves.toStrictEqual(wellKnownTransport);
|
||||
).resolves.toStrictEqual(configTransport);
|
||||
|
||||
expect(wellKnownFetcher).toHaveBeenCalledWith("example.org");
|
||||
expect(wellKnownFetcher).not.toHaveBeenCalled();
|
||||
},
|
||||
);
|
||||
|
||||
it("falls back to well-known only when the backend returns 404 M_UNRECOGNIZED", async () => {
|
||||
const client = makeClient();
|
||||
client._unstable_getRTCTransports.mockRejectedValue(notImplementedError);
|
||||
|
||||
const wellKnownFetcher = vi
|
||||
.fn<(domain: string) => Promise<IClientWellKnown>>()
|
||||
.mockResolvedValue(makeWellKnown([wellKnownTransport]));
|
||||
|
||||
const discovery = makeDiscovery({ client, wellKnownFetcher });
|
||||
|
||||
await expect(discovery.discoverPreferredTransport()).resolves.toStrictEqual(
|
||||
wellKnownTransport,
|
||||
);
|
||||
|
||||
expect(wellKnownFetcher).toHaveBeenCalledWith("example.org");
|
||||
});
|
||||
|
||||
it("does not fall back to well-known when the backend fails with a non-M_UNRECOGNIZED errcode", async () => {
|
||||
const client = makeClient();
|
||||
// Same 404 status as the not-implemented case, but a different errcode: only
|
||||
// M_UNRECOGNIZED is treated as "endpoint not implemented".
|
||||
client._unstable_getRTCTransports.mockRejectedValue(
|
||||
new MatrixError({ errcode: "M_UNKNOWN" }, 404),
|
||||
);
|
||||
|
||||
const wellKnownFetcher = vi
|
||||
.fn<(domain: string) => Promise<IClientWellKnown>>()
|
||||
.mockResolvedValue(makeWellKnown([wellKnownTransport]));
|
||||
|
||||
const discovery = makeDiscovery({ client, wellKnownFetcher });
|
||||
|
||||
await expect(discovery.discoverPreferredTransport()).resolves.toStrictEqual(
|
||||
configTransport,
|
||||
);
|
||||
|
||||
expect(wellKnownFetcher).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("skips backend discovery in widget mode and uses well-known", async () => {
|
||||
const client = makeClient();
|
||||
// widget mode is detected by the absence of an access token
|
||||
@@ -173,12 +222,7 @@ describe("RtcTransportAutoDiscovery", () => {
|
||||
.fn<(domain: string) => Promise<IClientWellKnown>>()
|
||||
.mockResolvedValue(makeWellKnown([wellKnownTransport]));
|
||||
|
||||
const discovery = new RtcTransportAutoDiscovery({
|
||||
client,
|
||||
resolvedConfig: makeResolvedConfig("https://config.example.org"),
|
||||
wellKnownFetcher,
|
||||
logger: rootLogger,
|
||||
});
|
||||
const discovery = makeDiscovery({ client, wellKnownFetcher });
|
||||
|
||||
await expect(discovery.discoverPreferredTransport()).resolves.toStrictEqual(
|
||||
wellKnownTransport,
|
||||
@@ -188,44 +232,58 @@ describe("RtcTransportAutoDiscovery", () => {
|
||||
expect(wellKnownFetcher).toHaveBeenCalledWith("example.org");
|
||||
});
|
||||
|
||||
it("falls back to app config when backend fails and well-known has no rtc_foci", async () => {
|
||||
it("does not make well-known lookups when disabled by config, even in widget mode", async () => {
|
||||
const client = makeClient();
|
||||
client._unstable_getRTCTransports.mockRejectedValue(
|
||||
new MatrixError({ errcode: "M_UNKNOWN" }, 404),
|
||||
// widget mode: backend endpoint is not attempted
|
||||
client.getAccessToken.mockReturnValue(null);
|
||||
|
||||
const wellKnownFetcher = vi
|
||||
.fn<(domain: string) => Promise<IClientWellKnown>>()
|
||||
.mockResolvedValue(makeWellKnown([wellKnownTransport]));
|
||||
|
||||
const discovery = makeDiscovery({
|
||||
client,
|
||||
wellKnownFetcher,
|
||||
enableClientWellKnownLookups: false,
|
||||
});
|
||||
|
||||
// The legacy well-known fallback is skipped entirely; only app config is used.
|
||||
await expect(discovery.discoverPreferredTransport()).resolves.toStrictEqual(
|
||||
configTransport,
|
||||
);
|
||||
|
||||
expect(wellKnownFetcher).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("falls back to app config when the backend is not implemented and well-known has no rtc_foci", async () => {
|
||||
const client = makeClient();
|
||||
client._unstable_getRTCTransports.mockRejectedValue(notImplementedError);
|
||||
|
||||
const wellKnownFetcher = vi
|
||||
.fn<(domain: string) => Promise<IClientWellKnown>>()
|
||||
.mockResolvedValue({} as IClientWellKnown);
|
||||
|
||||
const discovery = new RtcTransportAutoDiscovery({
|
||||
client,
|
||||
resolvedConfig: makeResolvedConfig("https://config.example.org"),
|
||||
wellKnownFetcher,
|
||||
logger: rootLogger,
|
||||
});
|
||||
const discovery = makeDiscovery({ client, wellKnownFetcher });
|
||||
|
||||
await expect(discovery.discoverPreferredTransport()).resolves.toStrictEqual(
|
||||
{
|
||||
type: "livekit",
|
||||
livekit_service_url: "https://config.example.org",
|
||||
},
|
||||
configTransport,
|
||||
);
|
||||
|
||||
expect(wellKnownFetcher).toHaveBeenCalledWith("example.org");
|
||||
});
|
||||
|
||||
it("returns null when backend, well-known and config are all unavailable", async () => {
|
||||
const client = makeClient();
|
||||
client._unstable_getRTCTransports.mockResolvedValue([]);
|
||||
client._unstable_getRTCTransports.mockRejectedValue(notImplementedError);
|
||||
|
||||
const wellKnownFetcher = vi
|
||||
.fn<(domain: string) => Promise<IClientWellKnown>>()
|
||||
.mockResolvedValue({} as IClientWellKnown);
|
||||
|
||||
const discovery = new RtcTransportAutoDiscovery({
|
||||
const discovery = makeDiscovery({
|
||||
client,
|
||||
resolvedConfig: makeResolvedConfig(undefined),
|
||||
wellKnownFetcher,
|
||||
logger: rootLogger,
|
||||
resolvedConfig: makeResolvedConfig(undefined),
|
||||
});
|
||||
|
||||
await expect(discovery.discoverPreferredTransport()).resolves.toBeNull();
|
||||
|
||||
@@ -8,7 +8,11 @@ import {
|
||||
isLivekitTransportConfig,
|
||||
type LivekitTransportConfig,
|
||||
} from "matrix-js-sdk/lib/matrixrtc";
|
||||
import { type IClientWellKnown, type MatrixClient } from "matrix-js-sdk";
|
||||
import {
|
||||
type IClientWellKnown,
|
||||
type MatrixClient,
|
||||
MatrixError,
|
||||
} from "matrix-js-sdk";
|
||||
import { type Logger } from "matrix-js-sdk/lib/logger";
|
||||
|
||||
import type { ResolvedConfigOptions } from "../../../config/ConfigOptions.ts";
|
||||
@@ -22,13 +26,36 @@ type TransportDiscoveryClient = Pick<
|
||||
export interface RtcTransportAutoDiscoveryProps {
|
||||
client: TransportDiscoveryClient;
|
||||
resolvedConfig: ResolvedConfigOptions;
|
||||
/**
|
||||
* Whether client `.well-known` lookups against the homeserver's `server_name`
|
||||
* are allowed. When false, the legacy MatrixRTC foci `.well-known` fallback is
|
||||
* skipped entirely and only the backend endpoint and app config are used.
|
||||
*/
|
||||
enableClientWellKnownLookups: boolean;
|
||||
wellKnownFetcher: (domain: string) => Promise<IClientWellKnown>;
|
||||
logger: Logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* The outcome of querying the backend `/rtc/transports` endpoint.
|
||||
*/
|
||||
interface BackendTransportResult {
|
||||
/** The livekit transport found via the backend endpoint, if any. */
|
||||
transport: LivekitTransportConfig | null;
|
||||
/**
|
||||
* Whether the caller may fall back to the legacy `.well-known` lookup. True
|
||||
* only when the backend endpoint is unavailable, i.e. it was not attempted
|
||||
* (widget mode) or the homeserver does not implement it (404 M_UNRECOGNIZED).
|
||||
* False when the endpoint answered authoritatively (a response without a
|
||||
* livekit transport) or failed with any other error.
|
||||
*/
|
||||
mayFallBackToWellKnown: boolean;
|
||||
}
|
||||
|
||||
export class RtcTransportAutoDiscovery {
|
||||
private readonly client: TransportDiscoveryClient;
|
||||
private readonly resolvedConfig: ResolvedConfigOptions;
|
||||
private readonly enableClientWellKnownLookups: boolean;
|
||||
private readonly wellKnownFetcher: (
|
||||
domain: string,
|
||||
) => Promise<IClientWellKnown>;
|
||||
@@ -37,38 +64,53 @@ export class RtcTransportAutoDiscovery {
|
||||
public constructor({
|
||||
client,
|
||||
resolvedConfig,
|
||||
enableClientWellKnownLookups,
|
||||
wellKnownFetcher,
|
||||
logger,
|
||||
}: RtcTransportAutoDiscoveryProps) {
|
||||
this.client = client;
|
||||
this.resolvedConfig = resolvedConfig;
|
||||
this.enableClientWellKnownLookups = enableClientWellKnownLookups;
|
||||
this.wellKnownFetcher = wellKnownFetcher;
|
||||
this.logger = logger.getChild("[RtcTransportAutoDiscovery]");
|
||||
}
|
||||
|
||||
public async discoverPreferredTransport(): Promise<LivekitTransportConfig | null> {
|
||||
// 1) backend transports
|
||||
const backendTransport = await this.tryBackendTransports();
|
||||
if (backendTransport) {
|
||||
const backend = await this.tryBackendTransports();
|
||||
if (backend.transport) {
|
||||
this.logger.info(
|
||||
`Found backend transport: ${backendTransport.livekit_service_url}`,
|
||||
`Found backend transport: ${backend.transport.livekit_service_url}`,
|
||||
);
|
||||
return backendTransport;
|
||||
return backend.transport;
|
||||
}
|
||||
|
||||
this.logger.info("No backend transport found, falling back to well-known");
|
||||
// 2) .well-known transports
|
||||
const wellKnownTransport = await this.tryWellKnownTransports();
|
||||
if (wellKnownTransport) {
|
||||
// Only consulted when the backend endpoint was inconclusive (not attempted
|
||||
// or not implemented) and client `.well-known` lookups are enabled. This
|
||||
// avoids contacting the homeserver's `server_name` both when a modern
|
||||
// endpoint has already given an authoritative answer and when lookups are
|
||||
// disabled by config.
|
||||
if (backend.mayFallBackToWellKnown && this.enableClientWellKnownLookups) {
|
||||
this.logger.info(
|
||||
`Found .well-known transport: ${wellKnownTransport.livekit_service_url}`,
|
||||
"No backend transport found, falling back to well-known",
|
||||
);
|
||||
const wellKnownTransport = await this.tryWellKnownTransports();
|
||||
if (wellKnownTransport) {
|
||||
this.logger.info(
|
||||
`Found .well-known transport: ${wellKnownTransport.livekit_service_url}`,
|
||||
);
|
||||
return wellKnownTransport;
|
||||
}
|
||||
} else {
|
||||
this.logger.info(
|
||||
this.enableClientWellKnownLookups
|
||||
? "Skipping .well-known lookup: backend endpoint gave an authoritative response"
|
||||
: "Skipping .well-known lookup: client well-known lookups are disabled",
|
||||
);
|
||||
return wellKnownTransport;
|
||||
}
|
||||
|
||||
this.logger.info(
|
||||
"No .well-known transport found, falling back to app config",
|
||||
);
|
||||
this.logger.info("Falling back to app config");
|
||||
|
||||
// 3) app config URL
|
||||
const configTransport = this.tryConfigTransport();
|
||||
@@ -87,7 +129,7 @@ export class RtcTransportAutoDiscovery {
|
||||
* This will not throw errors, but instead just log them and return null if the expected config is not found or malformed.
|
||||
* @private
|
||||
*/
|
||||
private async tryBackendTransports(): Promise<LivekitTransportConfig | null> {
|
||||
private async tryBackendTransports(): Promise<BackendTransportResult> {
|
||||
const client = this.client;
|
||||
// MSC4143: Attempt to fetch transports from backend.
|
||||
// TODO: Workaround for an issue in the js-sdk RoomWidgetClient that
|
||||
@@ -104,21 +146,36 @@ export class RtcTransportAutoDiscovery {
|
||||
);
|
||||
const first = transportList.find(isLivekitTransportConfig);
|
||||
if (first) {
|
||||
return first;
|
||||
} else {
|
||||
this.logger.info(
|
||||
`No livekit transport found in getRTCTransports end point`,
|
||||
transportList,
|
||||
);
|
||||
return { transport: first, mayFallBackToWellKnown: false };
|
||||
}
|
||||
// The homeserver implements the endpoint but returned no livekit
|
||||
// transport. This is an authoritative answer, so we do not fall back
|
||||
// to the legacy `.well-known` lookup.
|
||||
this.logger.info(
|
||||
`No livekit transport found in getRTCTransports end point`,
|
||||
transportList,
|
||||
);
|
||||
return { transport: null, mayFallBackToWellKnown: false };
|
||||
} catch (ex) {
|
||||
this.logger.info(`Failed to use getRTCTransports end point: ${ex}`);
|
||||
// Only a 404 M_UNRECOGNIZED means the homeserver does not implement the
|
||||
// endpoint, which is the one case where falling back to the legacy
|
||||
// `.well-known` lookup is appropriate. Any other error is transient or
|
||||
// unexpected, so we do not fall back.
|
||||
if (ex instanceof MatrixError && ex.errcode === "M_UNRECOGNIZED") {
|
||||
this.logger.info(
|
||||
"getRTCTransports end point not implemented by homeserver",
|
||||
);
|
||||
return { transport: null, mayFallBackToWellKnown: true };
|
||||
}
|
||||
this.logger.warn(`Failed to use getRTCTransports end point: ${ex}`);
|
||||
return { transport: null, mayFallBackToWellKnown: false };
|
||||
}
|
||||
} else {
|
||||
this.logger.debug(`getRTCTransports end point not available`);
|
||||
}
|
||||
|
||||
return null;
|
||||
// Not attempted (e.g. widget mode with no access token). Preserve the
|
||||
// existing behaviour of allowing the legacy `.well-known` lookup.
|
||||
this.logger.debug(`getRTCTransports end point not available`);
|
||||
return { transport: null, mayFallBackToWellKnown: true };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -166,7 +166,13 @@ export async function initClient(
|
||||
// Otherwise, a sync may complete before the listener gets applied,
|
||||
// and we will miss it.
|
||||
const syncPromise = waitForSync(client);
|
||||
await client.startClient({ clientWellKnownPollPeriod: 60 * 10 });
|
||||
// Leaving `clientWellKnownPollPeriod` unset stops the SDK polling the
|
||||
// homeserver's `server_name` `.well-known` after login.
|
||||
await client.startClient({
|
||||
clientWellKnownPollPeriod: Config.clientWellKnownLookupsEnabled()
|
||||
? 60 * 10
|
||||
: undefined,
|
||||
});
|
||||
await syncPromise;
|
||||
|
||||
return client;
|
||||
|
||||
@@ -19,6 +19,7 @@ const createRoomWidgetClientSpy = vi.mocked(createRoomWidgetClient);
|
||||
vi.mock("./config/Config", () => ({
|
||||
Config: {
|
||||
init: vi.fn().mockImplementation(async () => Promise.resolve()),
|
||||
clientWellKnownLookupsEnabled: vi.fn().mockReturnValue(true),
|
||||
},
|
||||
}));
|
||||
const configInitSpy = vi.mocked(Config.init);
|
||||
|
||||
@@ -195,7 +195,13 @@ export const initializeWidget = (
|
||||
// Wait for the config file to be ready (we load very early on so it might not
|
||||
// be otherwise)
|
||||
await Config.init();
|
||||
await client.startClient({ clientWellKnownPollPeriod: 60 * 10 });
|
||||
// Leaving `clientWellKnownPollPeriod` unset stops the SDK polling the
|
||||
// homeserver's `server_name` `.well-known` after login.
|
||||
await client.startClient({
|
||||
clientWellKnownPollPeriod: Config.clientWellKnownLookupsEnabled()
|
||||
? 60 * 10
|
||||
: undefined,
|
||||
});
|
||||
return client;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user