Skip to content

Commit

Permalink
Revert "MSC3575 (Sliding Sync) add well-known proxy support (#12307)"
Browse files Browse the repository at this point in the history
This reverts commit bb4f575.
  • Loading branch information
richvdh committed May 2, 2024
1 parent 374cee9 commit 166ec7b
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 154 deletions.
14 changes: 11 additions & 3 deletions src/MatrixClientPeg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,9 +295,17 @@ class MatrixClientPegClass implements IMatrixClientPeg {
opts.threadSupport = true;

if (SettingsStore.getValue("feature_sliding_sync")) {
opts.slidingSync = await SlidingSyncManager.instance.setup(this.matrixClient);
} else {
SlidingSyncManager.instance.checkSupport(this.matrixClient);
const proxyUrl = SettingsStore.getValue("feature_sliding_sync_proxy_url");
if (proxyUrl) {
logger.log("Activating sliding sync using proxy at ", proxyUrl);
} else {
logger.log("Activating sliding sync");
}
opts.slidingSync = SlidingSyncManager.instance.configure(
this.matrixClient,
proxyUrl || this.matrixClient.baseUrl,
);
SlidingSyncManager.instance.startSpidering(100, 50); // 100 rooms at a time, 50ms apart
}

// Connect the matrix client to the dispatcher and setting handlers
Expand Down
94 changes: 1 addition & 93 deletions src/SlidingSyncManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ limitations under the License.
* list ops)
*/

import { MatrixClient, EventType, AutoDiscovery, Method, timeoutSignal } from "matrix-js-sdk/src/matrix";
import { MatrixClient, EventType } from "matrix-js-sdk/src/matrix";
import {
MSC3575Filter,
MSC3575List,
Expand All @@ -56,9 +56,6 @@ import {
import { logger } from "matrix-js-sdk/src/logger";
import { defer, sleep } from "matrix-js-sdk/src/utils";

import SettingsStore from "./settings/SettingsStore";
import SlidingSyncController from "./settings/controllers/SlidingSyncController";

// how long to long poll for
const SLIDING_SYNC_TIMEOUT_MS = 20 * 1000;

Expand Down Expand Up @@ -326,93 +323,4 @@ export class SlidingSyncManager {
firstTime = false;
}
}

/**
* Set up the Sliding Sync instance; configures the end point and starts spidering.
* The sliding sync endpoint is derived the following way:
* 1. The user-defined sliding sync proxy URL (legacy, for backwards compatibility)
* 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)
* 3. The homeserver base url (for native server support)
* @param client The MatrixClient to use
* @returns A working Sliding Sync or undefined
*/
public async setup(client: MatrixClient): Promise<SlidingSync | undefined> {
const baseUrl = client.baseUrl;
const proxyUrl = SettingsStore.getValue("feature_sliding_sync_proxy_url");
const wellKnownProxyUrl = await this.getProxyFromWellKnown(client);

const slidingSyncEndpoint = proxyUrl || wellKnownProxyUrl || baseUrl;

this.configure(client, slidingSyncEndpoint);
logger.info("Sliding sync activated at", slidingSyncEndpoint);
this.startSpidering(100, 50); // 100 rooms at a time, 50ms apart

return this.slidingSync;
}

/**
* Get the sliding sync proxy URL from the client well known
* @param client The MatrixClient to use
* @return The proxy url
*/
public async getProxyFromWellKnown(client: MatrixClient): Promise<string | undefined> {
let proxyUrl: string | undefined;

try {
const clientWellKnown = await AutoDiscovery.findClientConfig(client.baseUrl);
proxyUrl = clientWellKnown?.["org.matrix.msc3575.proxy"]?.url;
} catch (e) {
// client.baseUrl is invalid, `AutoDiscovery.findClientConfig` has thrown
}

if (proxyUrl != undefined) {
logger.log("getProxyFromWellKnown: client well-known declares sliding sync proxy at", proxyUrl);
}
return proxyUrl;
}

/**
* Check if the server "natively" supports sliding sync (at the unstable endpoint).
* @param client The MatrixClient to use
* @return Whether the "native" (unstable) endpoint is up
*/
public async nativeSlidingSyncSupport(client: MatrixClient): Promise<boolean> {
try {
await client.http.authedRequest<void>(Method.Post, "/sync", undefined, undefined, {
localTimeoutMs: 10 * 1000, // 10s
prefix: "/_matrix/client/unstable/org.matrix.msc3575",
});
} catch (e) {
return false; // 404, M_UNRECOGNIZED
}

logger.log("nativeSlidingSyncSupport: sliding sync endpoint is up");
return true; // 200, OK
}

/**
* Check whether our homeserver has sliding sync support, that the endpoint is up, and
* is a sliding sync endpoint.
*
* Sets static member `SlidingSyncController.serverSupportsSlidingSync`
* @param client The MatrixClient to use
*/
public async checkSupport(client: MatrixClient): Promise<void> {
if (await this.nativeSlidingSyncSupport(client)) {
SlidingSyncController.serverSupportsSlidingSync = true;
return;
}

const proxyUrl = await this.getProxyFromWellKnown(client);
if (proxyUrl != undefined) {
const response = await fetch(proxyUrl + "/client/server.json", {
method: Method.Get,
signal: timeoutSignal(10 * 1000), // 10s
});
if (response.status === 200) {
logger.log("checkSupport: well-known sliding sync proxy is up at", proxyUrl);
SlidingSyncController.serverSupportsSlidingSync = true;
}
}
}
}
142 changes: 142 additions & 0 deletions src/components/views/dialogs/SlidingSyncOptionsDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import React from "react";
import { MatrixClient, Method } from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";

import { _t } from "../../../languageHandler";
import SettingsStore from "../../../settings/SettingsStore";
import TextInputDialog from "./TextInputDialog";
import withValidation from "../elements/Validation";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import { useAsyncMemo } from "../../../hooks/useAsyncMemo";
import { SettingLevel } from "../../../settings/SettingLevel";

/**
* Check that the server natively supports sliding sync.
* @param cli The MatrixClient of the logged in user.
* @throws if the proxy server is unreachable or not configured to the given homeserver
*/
async function syncHealthCheck(cli: MatrixClient): Promise<void> {
await cli.http.authedRequest(Method.Post, "/sync", undefined, undefined, {
localTimeoutMs: 10 * 1000, // 10s
prefix: "/_matrix/client/unstable/org.matrix.msc3575",
});
logger.info("server natively support sliding sync OK");
}

/**
* Check that the proxy url is in fact a sliding sync proxy endpoint and it is up.
* @param endpoint The proxy endpoint url
* @param hsUrl The homeserver url of the logged in user.
* @throws if the proxy server is unreachable or not configured to the given homeserver
*/
async function proxyHealthCheck(endpoint: string, hsUrl?: string): Promise<void> {
const controller = new AbortController();
const id = window.setTimeout(() => controller.abort(), 10 * 1000); // 10s
const res = await fetch(endpoint + "/client/server.json", {
signal: controller.signal,
});
clearTimeout(id);
if (res.status != 200) {
throw new Error(`proxyHealthCheck: proxy server returned HTTP ${res.status}`);
}
const body = await res.json();
if (body.server !== hsUrl) {
throw new Error(`proxyHealthCheck: client using ${hsUrl} but server is as ${body.server}`);
}
logger.info("sliding sync proxy is OK");
}

export const SlidingSyncOptionsDialog: React.FC<{ onFinished(enabled: boolean): void }> = ({ onFinished }) => {
const cli = MatrixClientPeg.safeGet();
const currentProxy = SettingsStore.getValue("feature_sliding_sync_proxy_url");
const hasNativeSupport = useAsyncMemo(
() =>
syncHealthCheck(cli).then(
() => true,
() => false,
),
[],
null,
);

let nativeSupport: string;
if (hasNativeSupport === null) {
nativeSupport = _t("labs|sliding_sync_checking");
} else {
nativeSupport = hasNativeSupport
? _t("labs|sliding_sync_server_support")
: _t("labs|sliding_sync_server_no_support");
}

const validProxy = withValidation<undefined, { error?: unknown }>({
async deriveData({ value }): Promise<{ error?: unknown }> {
if (!value) return {};
try {
await proxyHealthCheck(value, MatrixClientPeg.safeGet().baseUrl);
return {};
} catch (error) {
return { error };
}
},
rules: [
{
key: "required",
test: async ({ value }) => !!value || !!hasNativeSupport,
invalid: () => _t("labs|sliding_sync_server_specify_proxy"),
},
{
key: "working",
final: true,
test: async (_, { error }) => !error,
valid: () => _t("spotlight|public_rooms|network_dropdown_available_valid"),
invalid: ({ error }) => (error instanceof Error ? error.message : null),
},
],
});

return (
<TextInputDialog
title={_t("labs|sliding_sync_configuration")}
description={
<div>
<div>
<b>{_t("labs|sliding_sync_disable_warning")}</b>
</div>
{nativeSupport}
</div>
}
placeholder={
hasNativeSupport
? _t("labs|sliding_sync_proxy_url_optional_label")
: _t("labs|sliding_sync_proxy_url_label")
}
value={currentProxy}
button={_t("action|enable")}
validator={validProxy}
onFinished={(enable, proxyUrl) => {
if (enable) {
SettingsStore.setValue("feature_sliding_sync_proxy_url", null, SettingLevel.DEVICE, proxyUrl);
onFinished(true);
} else {
onFinished(false);
}
}}
/>
);
};
9 changes: 8 additions & 1 deletion src/i18n/strings/en_EN.json
Original file line number Diff line number Diff line change
Expand Up @@ -1460,9 +1460,16 @@
"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!",
"rust_crypto_requires_logout": "Once enabled, Rust cryptography can only be disabled by logging out and in again",
"sliding_sync": "Sliding Sync mode",
"sliding_sync_checking": "Checking…",
"sliding_sync_configuration": "Sliding Sync configuration",
"sliding_sync_description": "Under active development, cannot be disabled.",
"sliding_sync_disable_warning": "To disable you will need to log out and back in, use with caution!",
"sliding_sync_disabled_notice": "Log out and back in to disable",
"sliding_sync_server_no_support": "Your server lacks support",
"sliding_sync_proxy_url_label": "Proxy URL",
"sliding_sync_proxy_url_optional_label": "Proxy URL (optional)",
"sliding_sync_server_no_support": "Your server lacks native support",
"sliding_sync_server_specify_proxy": "Your server lacks native support, you must specify a proxy",
"sliding_sync_server_support": "Your server has native support",
"under_active_development": "Under active development.",
"unrealiable_e2e": "Unreliable in encrypted rooms",
"video_rooms": "Video rooms",
Expand Down
2 changes: 1 addition & 1 deletion src/settings/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ export const SETTINGS: { [setting: string]: ISetting } = {
controller: new SlidingSyncController(),
},
"feature_sliding_sync_proxy_url": {
// This is not a distinct feature, it is a legacy setting for feature_sliding_sync above
// This is not a distinct feature, it is a setting for feature_sliding_sync above
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
default: "",
},
Expand Down
13 changes: 8 additions & 5 deletions src/settings/controllers/SlidingSyncController.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Copyright 2024 Ed Geraghty <ed@geraghty.family>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -17,11 +16,18 @@ limitations under the License.

import SettingController from "./SettingController";
import PlatformPeg from "../../PlatformPeg";
import { SettingLevel } from "../SettingLevel";
import { SlidingSyncOptionsDialog } from "../../components/views/dialogs/SlidingSyncOptionsDialog";
import Modal from "../../Modal";
import SettingsStore from "../SettingsStore";
import { _t } from "../../languageHandler";

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

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

return false;
}
Expand Down
Loading

0 comments on commit 166ec7b

Please sign in to comment.