Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit bb4f575

Browse files
EdGeraghtyt3chguy
andauthored
MSC3575 (Sliding Sync) add well-known proxy support (#12307)
* Initial commit Signed-off-by: Ed Geraghty <ed@geraghty.family> * Remove commented code Signed-off-by: Ed Geraghty <ed@geraghty.family> * Change function to reflect it's proxy not native support Signed-off-by: Ed Geraghty <ed@geraghty.family> * Re-add check for servers with native support Signed-off-by: Ed Geraghty <ed@geraghty.family> * Add native support check back in Signed-off-by: Ed Geraghty <ed@geraghty.family> * Re-add endpoint health check function Signed-off-by: Ed Geraghty <ed@geraghty.family> * Use inbuilt `getWellKnown` function Signed-off-by: Ed Geraghty <ed@geraghty.family> * Change the error message to the correct function Signed-off-by: Ed Geraghty <ed@geraghty.family> * Stop storing the proxyurl in the settings for now Signed-off-by: Ed Geraghty <ed@geraghty.family> * Make the logger messages more useful Signed-off-by: Ed Geraghty <ed@geraghty.family> * Start moving the checking logic directly into the controller Signed-off-by: Ed Geraghty <ed@geraghty.family> * Add missing import Signed-off-by: Ed Geraghty <ed@geraghty.family> * Get the client rather than passing it in to the functions Signed-off-by: Ed Geraghty <ed@geraghty.family> * remove invalid `function` keyword Signed-off-by: Ed Geraghty <ed@geraghty.family> * Fix imports Signed-off-by: Ed Geraghty <ed@geraghty.family> * Our new functions are private We shouldn't(?) have to use these check in future elsewhere Signed-off-by: Ed Geraghty <ed@geraghty.family> * Change our proxy check function to return a boolean Signed-off-by: Ed Geraghty <ed@geraghty.family> * Make `nativeSlidingSyncSupport` also return boolean, add in health check Signed-off-by: Ed Geraghty <ed@geraghty.family> * Disable the sliding sync option if the server doesn't support Signed-off-by: Ed Geraghty <ed@geraghty.family> * Only enable the setting if it passes (again) Signed-off-by: Ed Geraghty <ed@geraghty.family> * Update our comments to better match what's going on Signed-off-by: Ed Geraghty <ed@geraghty.family> * Remove unused dialog Signed-off-by: Ed Geraghty <ed@geraghty.family> * Add a well-known check on start-up, if sliding sync has been enabled Signed-off-by: Ed Geraghty <ed@geraghty.family> * Check against the correct endpoint... Signed-off-by: Ed Geraghty <ed@geraghty.family> * Extract baseUrl as we'll reuse it Signed-off-by: Ed Geraghty <ed@geraghty.family> * Make the logs differentiate between the types of proxy Signed-off-by: Ed Geraghty <ed@geraghty.family> * Grab the client well-known directly for use Can't use the client object at this point, it hasn't read in the well-known Signed-off-by: Ed Geraghty <ed@geraghty.family> * Add myself to the copyright assignation I wrote the majority of this file... Signed-off-by: Ed Geraghty <ed@geraghty.family> * Only return `true` if it's actually there Signed-off-by: Ed Geraghty <ed@geraghty.family> * Correct the `proxySlidingSyncSupport` function comment to match the code Signed-off-by: Ed Geraghty <ed@geraghty.family> * Correct the `nativeSlidingSyncSupport`function comment to match the code Signed-off-by: Ed Geraghty <ed@geraghty.family> * Another comment/functionality paring Signed-off-by: Ed Geraghty <ed@geraghty.family> * Remove duplicated types from the doc Signed-off-by: Ed Geraghty <ed@geraghty.family> * Move await to the previous line Removes brackets, and corrects `wellKnown` from being a `Promise` Signed-off-by: Ed Geraghty <ed@geraghty.family> * use `waitForClientWellKnown` to avoid a race condition with the request Signed-off-by: Ed Geraghty <ed@geraghty.family> * Move getting the client out of the `if`, use `waitForClientWellKnown` Signed-off-by: Ed Geraghty <ed@geraghty.family> * Remove `beforeChange` override Signed-off-by: Ed Geraghty <ed@geraghty.family> * Move proxy setup logic into `SlidingSyncManager` Signed-off-by: Ed Geraghty <ed@geraghty.family> * Swap `configure` to private, we call it from `setup` which handles proxy Signed-off-by: Ed Geraghty <ed@geraghty.family> * Promises are always `true` TIL. Signed-off-by: Ed Geraghty <ed@geraghty.family> * use `timeoutSignal` Signed-off-by: Ed Geraghty <ed@geraghty.family> * Change message when there's no server support Signed-off-by: Ed Geraghty <ed@geraghty.family> * Refactor `slidingSyncHealthCheck` Signed-off-by: Ed Geraghty <ed@geraghty.family> * Refactor `nativeSlidingSyncSupport` with try/catch Signed-off-by: Ed Geraghty <ed@geraghty.family> * Change comment to hotlink Signed-off-by: Ed Geraghty <ed@geraghty.family> * Try and make the toggle disabled when there's no endpoint Signed-off-by: Ed Geraghty <ed@geraghty.family> * Move the if statement outside the refactored fn to avoid an await Signed-off-by: Ed Geraghty <ed@geraghty.family> * Revert "Swap `configure` to private, we call it from `setup` which handles proxy" This reverts commit c80a00b. * Remove unused import Signed-off-by: Ed Geraghty <ed@geraghty.family> * Further refactor `slidingSyncHealthCheck` `proxySlidingSyncSupport` already checks the client well-known is there Signed-off-by: Ed Geraghty <ed@geraghty.family> * Make `proxySlidingSyncSupport` log on success Signed-off-by: Ed Geraghty <ed@geraghty.family> * Clarify log message for proxy being up Signed-off-by: Ed Geraghty <ed@geraghty.family> * Move the logic into SlidingSyncManager All so we can set a static variable because the disabled check isn't asynchronous :) Signed-off-by: Ed Geraghty <ed@geraghty.family> * Obviously this isn't a return so don't overwrite with false! Signed-off-by: Ed Geraghty <ed@geraghty.family> * Remove outdated comment Signed-off-by: Ed Geraghty <ed@geraghty.family> * No need to pass in the client Signed-off-by: Ed Geraghty <ed@geraghty.family> * Activating SS should probably be info level logs Signed-off-by: Ed Geraghty <ed@geraghty.family> * If we've not enabled sliding sync, push the logs down a bit Signed-off-by: Ed Geraghty <ed@geraghty.family> * Update i18n error message Signed-off-by: Ed Geraghty <ed@geraghty.family> * Remove unused i18n strings Signed-off-by: Ed Geraghty <ed@geraghty.family> * Correct log message Signed-off-by: Ed Geraghty <ed@geraghty.family> * Prettier Signed-off-by: Ed Geraghty <ed@geraghty.family> * Remove many of the log messages Signed-off-by: Ed Geraghty <ed@geraghty.family> * Short out of `checkSupport` if it's `true` Signed-off-by: Ed Geraghty <ed@geraghty.family> * Add the endpoint back into the log when we're enabling it Signed-off-by: Ed Geraghty <ed@geraghty.family> * Note in the comment that `feature_sliding_sync_proxy_url` is legacy Signed-off-by: Ed Geraghty <ed@geraghty.family> * Expand the well-known liveness check log Signed-off-by: Ed Geraghty <ed@geraghty.family> * No need to stall the client waiting for sliding sync support * `AutoDiscovery.findClientConfig` throws if the baseUrl is blank * Fix `getProxyFromWellKnown` (?) * Add missing semicolon Sorry, linter! Signed-off-by: Ed Geraghty <ed@geraghty.family> * Pass our `MatrixClient` through instead of trying to grab it Signed-off-by: Ed Geraghty <ed@geraghty.family> * Add missing return in function comment Signed-off-by: Ed Geraghty <ed@geraghty.family> * Actually pass through our Client, not the Peg object Signed-off-by: Ed Geraghty <ed@geraghty.family> * Remove SonarCube smell complaint Signed-off-by: Ed Geraghty <ed@geraghty.family> * Neew to make our other two methods public to test Signed-off-by: Ed Geraghty <ed@geraghty.family> * First passing test Hurrah! Signed-off-by: Ed Geraghty <ed@geraghty.family> * Two more tests, this time on `checkSupport` Signed-off-by: Ed Geraghty <ed@geraghty.family> * Reset our `serverSupportsSlidingSync` between tests Signed-off-by: Ed Geraghty <ed@geraghty.family> * Check the static member is being set Signed-off-by: Ed Geraghty <ed@geraghty.family> * Move the static assignation down to the relevant tests Signed-off-by: Ed Geraghty <ed@geraghty.family> * Pull getProxyFromWellKnown mocking up Signed-off-by: Ed Geraghty <ed@geraghty.family> * Check we /haven't/ shorted out Signed-off-by: Ed Geraghty <ed@geraghty.family> * Move our spy up so we can reuse it Signed-off-by: Ed Geraghty <ed@geraghty.family> * Check spidering is being called Signed-off-by: Ed Geraghty <ed@geraghty.family> * Test the proxy is declared Signed-off-by: Ed Geraghty <ed@geraghty.family> * Test entered manually Signed-off-by: Ed Geraghty <ed@geraghty.family> * Sorry, linter * I guess these strings are wrong? * Replace any with string Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Ed Geraghty <ed@geraghty.family> Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
1 parent 641a20c commit bb4f575

File tree

7 files changed

+154
-171
lines changed

7 files changed

+154
-171
lines changed

src/MatrixClientPeg.ts

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -273,17 +273,9 @@ class MatrixClientPegClass implements IMatrixClientPeg {
273273
opts.threadSupport = true;
274274

275275
if (SettingsStore.getValue("feature_sliding_sync")) {
276-
const proxyUrl = SettingsStore.getValue("feature_sliding_sync_proxy_url");
277-
if (proxyUrl) {
278-
logger.log("Activating sliding sync using proxy at ", proxyUrl);
279-
} else {
280-
logger.log("Activating sliding sync");
281-
}
282-
opts.slidingSync = SlidingSyncManager.instance.configure(
283-
this.matrixClient,
284-
proxyUrl || this.matrixClient.baseUrl,
285-
);
286-
SlidingSyncManager.instance.startSpidering(100, 50); // 100 rooms at a time, 50ms apart
276+
opts.slidingSync = await SlidingSyncManager.instance.setup(this.matrixClient);
277+
} else {
278+
SlidingSyncManager.instance.checkSupport(this.matrixClient);
287279
}
288280

289281
// Connect the matrix client to the dispatcher and setting handlers

src/SlidingSyncManager.ts

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ limitations under the License.
4444
* list ops)
4545
*/
4646

47-
import { MatrixClient, EventType } from "matrix-js-sdk/src/matrix";
47+
import { MatrixClient, EventType, AutoDiscovery, Method, timeoutSignal } from "matrix-js-sdk/src/matrix";
4848
import {
4949
MSC3575Filter,
5050
MSC3575List,
@@ -56,6 +56,9 @@ import {
5656
import { logger } from "matrix-js-sdk/src/logger";
5757
import { defer, sleep } from "matrix-js-sdk/src/utils";
5858

59+
import SettingsStore from "./settings/SettingsStore";
60+
import SlidingSyncController from "./settings/controllers/SlidingSyncController";
61+
5962
// how long to long poll for
6063
const SLIDING_SYNC_TIMEOUT_MS = 20 * 1000;
6164

@@ -323,4 +326,93 @@ export class SlidingSyncManager {
323326
firstTime = false;
324327
}
325328
}
329+
330+
/**
331+
* Set up the Sliding Sync instance; configures the end point and starts spidering.
332+
* The sliding sync endpoint is derived the following way:
333+
* 1. The user-defined sliding sync proxy URL (legacy, for backwards compatibility)
334+
* 2. The client `well-known` sliding sync proxy URL [declared at the unstable prefix](https://github.com/matrix-org/matrix-spec-proposals/blob/kegan/sync-v3/proposals/3575-sync.md#unstable-prefix)
335+
* 3. The homeserver base url (for native server support)
336+
* @param client The MatrixClient to use
337+
* @returns A working Sliding Sync or undefined
338+
*/
339+
public async setup(client: MatrixClient): Promise<SlidingSync | undefined> {
340+
const baseUrl = client.baseUrl;
341+
const proxyUrl = SettingsStore.getValue("feature_sliding_sync_proxy_url");
342+
const wellKnownProxyUrl = await this.getProxyFromWellKnown(client);
343+
344+
const slidingSyncEndpoint = proxyUrl || wellKnownProxyUrl || baseUrl;
345+
346+
this.configure(client, slidingSyncEndpoint);
347+
logger.info("Sliding sync activated at", slidingSyncEndpoint);
348+
this.startSpidering(100, 50); // 100 rooms at a time, 50ms apart
349+
350+
return this.slidingSync;
351+
}
352+
353+
/**
354+
* Get the sliding sync proxy URL from the client well known
355+
* @param client The MatrixClient to use
356+
* @return The proxy url
357+
*/
358+
public async getProxyFromWellKnown(client: MatrixClient): Promise<string | undefined> {
359+
let proxyUrl: string | undefined;
360+
361+
try {
362+
const clientWellKnown = await AutoDiscovery.findClientConfig(client.baseUrl);
363+
proxyUrl = clientWellKnown?.["org.matrix.msc3575.proxy"]?.url;
364+
} catch (e) {
365+
// client.baseUrl is invalid, `AutoDiscovery.findClientConfig` has thrown
366+
}
367+
368+
if (proxyUrl != undefined) {
369+
logger.log("getProxyFromWellKnown: client well-known declares sliding sync proxy at", proxyUrl);
370+
}
371+
return proxyUrl;
372+
}
373+
374+
/**
375+
* Check if the server "natively" supports sliding sync (at the unstable endpoint).
376+
* @param client The MatrixClient to use
377+
* @return Whether the "native" (unstable) endpoint is up
378+
*/
379+
public async nativeSlidingSyncSupport(client: MatrixClient): Promise<boolean> {
380+
try {
381+
await client.http.authedRequest<void>(Method.Post, "/sync", undefined, undefined, {
382+
localTimeoutMs: 10 * 1000, // 10s
383+
prefix: "/_matrix/client/unstable/org.matrix.msc3575",
384+
});
385+
} catch (e) {
386+
return false; // 404, M_UNRECOGNIZED
387+
}
388+
389+
logger.log("nativeSlidingSyncSupport: sliding sync endpoint is up");
390+
return true; // 200, OK
391+
}
392+
393+
/**
394+
* Check whether our homeserver has sliding sync support, that the endpoint is up, and
395+
* is a sliding sync endpoint.
396+
*
397+
* Sets static member `SlidingSyncController.serverSupportsSlidingSync`
398+
* @param client The MatrixClient to use
399+
*/
400+
public async checkSupport(client: MatrixClient): Promise<void> {
401+
if (await this.nativeSlidingSyncSupport(client)) {
402+
SlidingSyncController.serverSupportsSlidingSync = true;
403+
return;
404+
}
405+
406+
const proxyUrl = await this.getProxyFromWellKnown(client);
407+
if (proxyUrl != undefined) {
408+
const response = await fetch(proxyUrl + "/client/server.json", {
409+
method: Method.Get,
410+
signal: timeoutSignal(10 * 1000), // 10s
411+
});
412+
if (response.status === 200) {
413+
logger.log("checkSupport: well-known sliding sync proxy is up at", proxyUrl);
414+
SlidingSyncController.serverSupportsSlidingSync = true;
415+
}
416+
}
417+
}
326418
}

src/components/views/dialogs/SlidingSyncOptionsDialog.tsx

Lines changed: 0 additions & 142 deletions
This file was deleted.

src/i18n/strings/en_EN.json

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,16 +1460,9 @@
14601460
"rust_crypto_optin_warning": "Switching to the Rust cryptography requires a migration process that may take several minutes. To disable you will need to log out and back in; use with caution!",
14611461
"rust_crypto_requires_logout": "Once enabled, Rust cryptography can only be disabled by logging out and in again",
14621462
"sliding_sync": "Sliding Sync mode",
1463-
"sliding_sync_checking": "Checking…",
1464-
"sliding_sync_configuration": "Sliding Sync configuration",
14651463
"sliding_sync_description": "Under active development, cannot be disabled.",
1466-
"sliding_sync_disable_warning": "To disable you will need to log out and back in, use with caution!",
14671464
"sliding_sync_disabled_notice": "Log out and back in to disable",
1468-
"sliding_sync_proxy_url_label": "Proxy URL",
1469-
"sliding_sync_proxy_url_optional_label": "Proxy URL (optional)",
1470-
"sliding_sync_server_no_support": "Your server lacks native support",
1471-
"sliding_sync_server_specify_proxy": "Your server lacks native support, you must specify a proxy",
1472-
"sliding_sync_server_support": "Your server has native support",
1465+
"sliding_sync_server_no_support": "Your server lacks support",
14731466
"under_active_development": "Under active development.",
14741467
"unrealiable_e2e": "Unreliable in encrypted rooms",
14751468
"video_rooms": "Video rooms",

src/settings/Settings.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ export const SETTINGS: { [setting: string]: ISetting } = {
407407
controller: new SlidingSyncController(),
408408
},
409409
"feature_sliding_sync_proxy_url": {
410-
// This is not a distinct feature, it is a setting for feature_sliding_sync above
410+
// This is not a distinct feature, it is a legacy setting for feature_sliding_sync above
411411
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
412412
default: "",
413413
},

src/settings/controllers/SlidingSyncController.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
22
Copyright 2022 The Matrix.org Foundation C.I.C.
3+
Copyright 2024 Ed Geraghty <ed@geraghty.family>
34
45
Licensed under the Apache License, Version 2.0 (the "License");
56
you may not use this file except in compliance with the License.
@@ -16,18 +17,11 @@ limitations under the License.
1617

1718
import SettingController from "./SettingController";
1819
import PlatformPeg from "../../PlatformPeg";
19-
import { SettingLevel } from "../SettingLevel";
20-
import { SlidingSyncOptionsDialog } from "../../components/views/dialogs/SlidingSyncOptionsDialog";
21-
import Modal from "../../Modal";
2220
import SettingsStore from "../SettingsStore";
2321
import { _t } from "../../languageHandler";
2422

2523
export default class SlidingSyncController extends SettingController {
26-
public async beforeChange(level: SettingLevel, roomId: string, newValue: any): Promise<boolean> {
27-
const { finished } = Modal.createDialog(SlidingSyncOptionsDialog);
28-
const [value] = await finished;
29-
return newValue === value; // abort the operation if we're already in the state the user chose via modal
30-
}
24+
public static serverSupportsSlidingSync: boolean;
3125

3226
public async onChange(): Promise<void> {
3327
PlatformPeg.get()?.reload();
@@ -38,6 +32,9 @@ export default class SlidingSyncController extends SettingController {
3832
if (SettingsStore.getValue("feature_sliding_sync")) {
3933
return _t("labs|sliding_sync_disabled_notice");
4034
}
35+
if (!SlidingSyncController.serverSupportsSlidingSync) {
36+
return _t("labs|sliding_sync_server_no_support");
37+
}
4138

4239
return false;
4340
}

0 commit comments

Comments
 (0)