Skip to content

Commit

Permalink
Merge pull request #2637 from element-hq/hs/workaround-federation-bug
Browse files Browse the repository at this point in the history
Retry a join on invite if the response was M_FORBIDDEN
  • Loading branch information
Half-Shot authored Sep 18, 2024
2 parents 745533f + c19645c commit 4bf6509
Showing 1 changed file with 44 additions and 5 deletions.
49 changes: 44 additions & 5 deletions src/room/useLoadGroupCall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { SyncState } from "matrix-js-sdk/src/sync";
import { MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc/MatrixRTCSession";
import { RoomEvent, Room } from "matrix-js-sdk/src/models/room";
import { KnownMembership } from "matrix-js-sdk/src/types";
import { JoinRule } from "matrix-js-sdk/src/matrix";
import { JoinRule, MatrixError } from "matrix-js-sdk/src/matrix";
import { useTranslation } from "react-i18next";

import { widget } from "../widget";
Expand Down Expand Up @@ -54,6 +54,42 @@ export type GroupCallStatus =
| GroupCallWaitForInvite
| GroupCallCanKnock;

const MAX_ATTEMPTS_FOR_INVITE_JOIN_FAILURE = 3;
const DELAY_MS_FOR_INVITE_JOIN_FAILURE = 3000;

/**
* Join a room, and retry on M_FORBIDDEN error in order to work
* around a potential race when joining rooms over federation.
*
* Will wait up to to `DELAY_MS_FOR_INVITE_JOIN_FAILURE` per attempt.
* Will try up to `MAX_ATTEMPTS_FOR_INVITE_JOIN_FAILURE` times.
*
* @see https://github.com/element-hq/element-call/issues/2634
* @param client The matrix client
* @param attempt Number of attempts made.
* @param params Parameters to pass to client.joinRoom
*/
async function joinRoomAfterInvite(
client: MatrixClient,
attempt = 0,
...params: Parameters<MatrixClient["joinRoom"]>
): ReturnType<MatrixClient["joinRoom"]> {
try {
return await client.joinRoom(...params);
} catch (ex) {
if (
ex instanceof MatrixError &&
ex.errcode === "M_FORBIDDEN" &&
attempt < MAX_ATTEMPTS_FOR_INVITE_JOIN_FAILURE
) {
// If we were invited and got a M_FORBIDDEN, it's highly likely the server hasn't caught up yet.
await new Promise((r) => setTimeout(r, DELAY_MS_FOR_INVITE_JOIN_FAILURE));
return joinRoomAfterInvite(client, attempt + 1, ...params);
}
throw ex;
}
}

export class CallTerminatedMessage extends Error {
/**
* @param messageBody The message explaining the kind of termination (kick, ban, knock reject, etc.) (translated)
Expand Down Expand Up @@ -162,10 +198,13 @@ export const useLoadGroupCall = (
membership === KnownMembership.Invite &&
prevMembership === KnownMembership.Knock
) {
client.joinRoom(room.roomId, { viaServers }).then((room) => {
logger.log("Auto-joined %s", room.roomId);
resolve(room);
}, reject);
joinRoomAfterInvite(client, 0, room.roomId, { viaServers }).then(
(room) => {
logger.log("Auto-joined %s", room.roomId);
resolve(room);
},
reject,
);
}
if (membership === KnownMembership.Ban) reject(bannedError());
if (membership === KnownMembership.Leave)
Expand Down

0 comments on commit 4bf6509

Please sign in to comment.