Skip to content

Commit 6971e7b

Browse files
committed
Merge branches 'toger5/local-echo' and 'af/rtc-handle-ratelimiting' into element-call-nov-preview
2 parents e6fa25d + b96ae12 commit 6971e7b

File tree

1 file changed

+64
-30
lines changed

1 file changed

+64
-30
lines changed

src/matrixrtc/MatrixRTCSession.ts

Lines changed: 64 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import { randomString, secureRandomBase64Url } from "../randomstring.ts";
3434
import { EncryptionKeysEventContent } from "./types.ts";
3535
import { decodeBase64, encodeUnpaddedBase64 } from "../base64.ts";
3636
import { KnownMembership } from "../@types/membership.ts";
37-
import { MatrixError, safeGetRetryAfterMs } from "../http-api/errors.ts";
37+
import { HTTPError, MatrixError, safeGetRetryAfterMs } from "../http-api/errors.ts";
3838
import { MatrixEvent } from "../models/event.ts";
3939
import { isLivekitFocusActive } from "./LivekitFocus.ts";
4040
import { ExperimentalGroupCallRoomMemberState } from "../webrtc/groupCall.ts";
@@ -1031,39 +1031,39 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
10311031
const prepareDelayedDisconnection = async (): Promise<void> => {
10321032
try {
10331033
// TODO: If delayed event times out, re-join!
1034-
const res = await this.client._unstable_sendDelayedStateEvent(
1035-
this.room.roomId,
1036-
{
1037-
delay: 8000,
1038-
},
1039-
EventType.GroupCallMemberPrefix,
1040-
{}, // leave event
1041-
stateKey,
1034+
const res = await resendIfRateLimited(() =>
1035+
this.client._unstable_sendDelayedStateEvent(
1036+
this.room.roomId,
1037+
{
1038+
delay: 8000,
1039+
},
1040+
EventType.GroupCallMemberPrefix,
1041+
{}, // leave event
1042+
stateKey,
1043+
),
10421044
);
10431045
this.disconnectDelayId = res.delay_id;
10441046
} catch (e) {
1045-
// TODO: Retry if rate-limited
10461047
logger.error("Failed to prepare delayed disconnection event:", e);
10471048
}
10481049
};
10491050
await prepareDelayedDisconnection();
10501051
// Send join event _after_ preparing the delayed disconnection event
1051-
await this.client.sendStateEvent(
1052-
this.room.roomId,
1053-
EventType.GroupCallMemberPrefix,
1054-
newContent,
1055-
stateKey,
1052+
await resendIfRateLimited(() =>
1053+
this.client.sendStateEvent(this.room.roomId, EventType.GroupCallMemberPrefix, newContent, stateKey),
10561054
);
10571055
// If sending state cancels your own delayed state, prepare another delayed state
10581056
// TODO: Remove this once MSC4140 is stable & doesn't cancel own delayed state
10591057
if (this.disconnectDelayId !== undefined) {
10601058
try {
1061-
await this.client._unstable_updateDelayedEvent(
1062-
this.disconnectDelayId,
1063-
UpdateDelayedEventAction.Restart,
1059+
const knownDisconnectDelayId = this.disconnectDelayId;
1060+
await resendIfRateLimited(() =>
1061+
this.client._unstable_updateDelayedEvent(
1062+
knownDisconnectDelayId,
1063+
UpdateDelayedEventAction.Restart,
1064+
),
10641065
);
10651066
} catch (e) {
1066-
// TODO: Make embedded client include errcode, and retry only if not M_NOT_FOUND (or rate-limited)
10671067
logger.warn("Failed to update delayed disconnection event, prepare it again:", e);
10681068
this.disconnectDelayId = undefined;
10691069
await prepareDelayedDisconnection();
@@ -1076,23 +1076,27 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
10761076
let sentDelayedDisconnect = false;
10771077
if (this.disconnectDelayId !== undefined) {
10781078
try {
1079-
await this.client._unstable_updateDelayedEvent(
1080-
this.disconnectDelayId,
1081-
UpdateDelayedEventAction.Send,
1079+
const knownDisconnectDelayId = this.disconnectDelayId;
1080+
await resendIfRateLimited(() =>
1081+
this.client._unstable_updateDelayedEvent(
1082+
knownDisconnectDelayId,
1083+
UpdateDelayedEventAction.Send,
1084+
),
10821085
);
10831086
sentDelayedDisconnect = true;
10841087
} catch (e) {
1085-
// TODO: Retry if rate-limited
10861088
logger.error("Failed to send our delayed disconnection event:", e);
10871089
}
10881090
this.disconnectDelayId = undefined;
10891091
}
10901092
if (!sentDelayedDisconnect) {
1091-
await this.client.sendStateEvent(
1092-
this.room.roomId,
1093-
EventType.GroupCallMemberPrefix,
1094-
{},
1095-
this.makeMembershipStateKey(localUserId, localDeviceId),
1093+
await resendIfRateLimited(() =>
1094+
this.client.sendStateEvent(
1095+
this.room.roomId,
1096+
EventType.GroupCallMemberPrefix,
1097+
{},
1098+
this.makeMembershipStateKey(localUserId, localDeviceId),
1099+
),
10961100
);
10971101
}
10981102
}
@@ -1111,10 +1115,12 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
11111115

11121116
private readonly delayDisconnection = async (): Promise<void> => {
11131117
try {
1114-
await this.client._unstable_updateDelayedEvent(this.disconnectDelayId!, UpdateDelayedEventAction.Restart);
1118+
const knownDisconnectDelayId = this.disconnectDelayId!;
1119+
await resendIfRateLimited(() =>
1120+
this.client._unstable_updateDelayedEvent(knownDisconnectDelayId, UpdateDelayedEventAction.Restart),
1121+
);
11151122
this.scheduleDelayDisconnection();
11161123
} catch (e) {
1117-
// TODO: Retry if rate-limited
11181124
logger.error("Failed to delay our disconnection event:", e);
11191125
}
11201126
};
@@ -1162,3 +1168,31 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
11621168
this.sendEncryptionKeysEvent(newKeyIndex);
11631169
};
11641170
}
1171+
1172+
async function resendIfRateLimited<T>(func: () => Promise<T>, numRetriesAllowed: number = 1): Promise<T> {
1173+
// eslint-disable-next-line no-constant-condition
1174+
while (true) {
1175+
try {
1176+
return await func();
1177+
} catch (e) {
1178+
if (numRetriesAllowed > 0 && e instanceof HTTPError && e.isRateLimitError()) {
1179+
numRetriesAllowed--;
1180+
let resendDelay: number;
1181+
const defaultMs = 5000;
1182+
try {
1183+
resendDelay = e.getRetryAfterMs() ?? defaultMs;
1184+
logger.info(`Rate limited by server, retrying in ${resendDelay}ms`);
1185+
} catch (e) {
1186+
logger.warn(
1187+
`Error while retrieving a rate-limit retry delay, retrying after default delay of ${defaultMs}`,
1188+
e,
1189+
);
1190+
resendDelay = defaultMs;
1191+
}
1192+
await sleep(resendDelay);
1193+
} else {
1194+
throw e;
1195+
}
1196+
}
1197+
}
1198+
}

0 commit comments

Comments
 (0)