From 3a5b7c1d0ed4939e9e6b23b621da7c83a3140f6d Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 23 Aug 2021 18:47:36 +0200 Subject: [PATCH] support well-known lookup --- src/matrix/SessionContainer.js | 22 +++++++++--- src/matrix/well-known.js | 64 ++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 src/matrix/well-known.js diff --git a/src/matrix/SessionContainer.js b/src/matrix/SessionContainer.js index ab20a4eb..7233eeb0 100644 --- a/src/matrix/SessionContainer.js +++ b/src/matrix/SessionContainer.js @@ -1,5 +1,6 @@ /* Copyright 2020 Bruno Windels +Copyright 2020, 2021 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. @@ -15,6 +16,7 @@ limitations under the License. */ import {createEnum} from "../utils/enum.js"; +import {lookupHomeServer} from "./well-known.js"; import {AbortableOperation} from "../utils/AbortableOperation"; import {ObservableValue} from "../observable/ObservableValue.js"; import {HomeServerApi} from "./net/HomeServerApi.js"; @@ -36,6 +38,16 @@ function normalizeHomeserver(homeServer) { } } +function getRetryHomeServer(homeServer) { + const url = new URL(homeServer); + const {host} = url; + const dotCount = host.split(".").length - 1; + if (dotCount === 1) { + url.host = `www.${host}`; + return url.origin; + } +} + export const LoadStatus = createEnum( "NotLoading", "Login", @@ -108,7 +120,7 @@ export class SessionContainer { implements LoginMethod */ const flows = options.flows; - const result = {}; + const result = {homeServer}; for (const flow of flows) { if (flow.type === "m.login.password") { result.password = (username, password) => new PasswordLoginMethod({homeServer, username, password}); @@ -124,11 +136,13 @@ export class SessionContainer { } queryLogin(homeServer) { - const normalizedHS = normalizeHomeserver(homeServer); - const hsApi = new HomeServerApi({homeServer: normalizedHS, request: this._platform.request}); return new AbortableOperation(async setAbortable => { + homeServer = await lookupHomeServer(homeServer, (url, options) => { + return setAbortable(this._platform.request(url, options)); + }); + const hsApi = new HomeServerApi({homeServer, request: this._platform.request}); const response = await setAbortable(hsApi.getLoginFlows()).response(); - return this._parseLoginOptions(response, normalizedHS); + return this._parseLoginOptions(response, homeServer); }); } diff --git a/src/matrix/well-known.js b/src/matrix/well-known.js new file mode 100644 index 00000000..a33a734a --- /dev/null +++ b/src/matrix/well-known.js @@ -0,0 +1,64 @@ +/* +Copyright 2021 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. +*/ + +function normalizeHomeserver(homeServer) { + try { + return new URL(homeServer).origin; + } catch (err) { + return new URL(`https://${homeServer}`).origin; + } +} + +function getRetryHomeServer(homeServer) { + const url = new URL(homeServer); + const {host} = url; + const dotCount = host.split(".").length - 1; + if (dotCount === 1) { + url.host = `www.${host}`; + return url.origin; + } +} + +export async function lookupHomeServer(homeServer, request) { + homeServer = normalizeHomeserver(homeServer); + const requestOptions = {format: "json", timeout: 30000, method: "GET"}; + let wellKnownResponse = null; + while (!wellKnownResponse) { + try { + const wellKnownUrl = `${homeServer}/.well-known/matrix/client`; + wellKnownResponse = await request(wellKnownUrl, requestOptions).response(); + } catch (err) { + if (err.name === "ConnectionError") { + const retryHS = getRetryHomeServer(homeServer); + if (retryHS) { + homeServer = retryHS; + } else { + throw err; + } + } else { + throw err; + } + } + } + if (wellKnownResponse.status === 200) { + const {body} = wellKnownResponse; + const wellKnownHomeServer = body["m.homeserver"]?.["base_url"]; + if (typeof wellKnownHomeServer === "string") { + homeServer = normalizeHomeserver(wellKnownHomeServer); + } + } + return homeServer +}