@@ -34,7 +34,7 @@ import { randomString, secureRandomBase64Url } from "../randomstring.ts";
34
34
import { EncryptionKeysEventContent } from "./types.ts" ;
35
35
import { decodeBase64 , encodeUnpaddedBase64 } from "../base64.ts" ;
36
36
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" ;
38
38
import { MatrixEvent } from "../models/event.ts" ;
39
39
import { isLivekitFocusActive } from "./LivekitFocus.ts" ;
40
40
import { ExperimentalGroupCallRoomMemberState } from "../webrtc/groupCall.ts" ;
@@ -1031,39 +1031,39 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
1031
1031
const prepareDelayedDisconnection = async ( ) : Promise < void > => {
1032
1032
try {
1033
1033
// 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
+ ) ,
1042
1044
) ;
1043
1045
this . disconnectDelayId = res . delay_id ;
1044
1046
} catch ( e ) {
1045
- // TODO: Retry if rate-limited
1046
1047
logger . error ( "Failed to prepare delayed disconnection event:" , e ) ;
1047
1048
}
1048
1049
} ;
1049
1050
await prepareDelayedDisconnection ( ) ;
1050
1051
// 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 ) ,
1056
1054
) ;
1057
1055
// If sending state cancels your own delayed state, prepare another delayed state
1058
1056
// TODO: Remove this once MSC4140 is stable & doesn't cancel own delayed state
1059
1057
if ( this . disconnectDelayId !== undefined ) {
1060
1058
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
+ ) ,
1064
1065
) ;
1065
1066
} catch ( e ) {
1066
- // TODO: Make embedded client include errcode, and retry only if not M_NOT_FOUND (or rate-limited)
1067
1067
logger . warn ( "Failed to update delayed disconnection event, prepare it again:" , e ) ;
1068
1068
this . disconnectDelayId = undefined ;
1069
1069
await prepareDelayedDisconnection ( ) ;
@@ -1076,23 +1076,27 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
1076
1076
let sentDelayedDisconnect = false ;
1077
1077
if ( this . disconnectDelayId !== undefined ) {
1078
1078
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
+ ) ,
1082
1085
) ;
1083
1086
sentDelayedDisconnect = true ;
1084
1087
} catch ( e ) {
1085
- // TODO: Retry if rate-limited
1086
1088
logger . error ( "Failed to send our delayed disconnection event:" , e ) ;
1087
1089
}
1088
1090
this . disconnectDelayId = undefined ;
1089
1091
}
1090
1092
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
+ ) ,
1096
1100
) ;
1097
1101
}
1098
1102
}
@@ -1111,10 +1115,12 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
1111
1115
1112
1116
private readonly delayDisconnection = async ( ) : Promise < void > => {
1113
1117
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
+ ) ;
1115
1122
this . scheduleDelayDisconnection ( ) ;
1116
1123
} catch ( e ) {
1117
- // TODO: Retry if rate-limited
1118
1124
logger . error ( "Failed to delay our disconnection event:" , e ) ;
1119
1125
}
1120
1126
} ;
@@ -1162,3 +1168,31 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
1162
1168
this . sendEncryptionKeysEvent ( newKeyIndex ) ;
1163
1169
} ;
1164
1170
}
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