@@ -34,7 +34,7 @@ import { randomString, secureRandomBase64Url } from "../randomstring.ts";
3434import { EncryptionKeysEventContent } from "./types.ts" ;
3535import { decodeBase64 , encodeUnpaddedBase64 } from "../base64.ts" ;
3636import { KnownMembership } from "../@types/membership.ts" ;
37- import { MatrixError , safeGetRetryAfterMs } from "../http-api/errors.ts" ;
37+ import { HTTPError , MatrixError , safeGetRetryAfterMs } from "../http-api/errors.ts" ;
3838import { MatrixEvent } from "../models/event.ts" ;
3939import { isLivekitFocusActive } from "./LivekitFocus.ts" ;
4040import { ExperimentalGroupCallRoomMemberState } from "../webrtc/groupCall.ts" ;
@@ -1031,18 +1031,17 @@ 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 (
1034+ const res = await resendIfRateLimited ( ( ) => this . client . _unstable_sendDelayedStateEvent (
10351035 this . room . roomId ,
10361036 {
10371037 delay : 8000 ,
10381038 } ,
10391039 EventType . GroupCallMemberPrefix ,
10401040 { } , // leave event
10411041 stateKey ,
1042- ) ;
1042+ ) ) ;
10431043 this . disconnectDelayId = res . delay_id ;
10441044 } catch ( e ) {
1045- // TODO: Retry if rate-limited
10461045 logger . error ( "Failed to prepare delayed disconnection event:" , e ) ;
10471046 }
10481047 } ;
@@ -1058,12 +1057,12 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
10581057 // TODO: Remove this once MSC4140 is stable & doesn't cancel own delayed state
10591058 if ( this . disconnectDelayId !== undefined ) {
10601059 try {
1061- await this . client . _unstable_updateDelayedEvent (
1062- this . disconnectDelayId ,
1060+ const knownDisconnectDelayId = this . disconnectDelayId ;
1061+ await resendIfRateLimited ( ( ) => this . client . _unstable_updateDelayedEvent (
1062+ knownDisconnectDelayId ,
10631063 UpdateDelayedEventAction . Restart ,
1064- ) ;
1064+ ) ) ;
10651065 } catch ( e ) {
1066- // TODO: Make embedded client include errcode, and retry only if not M_NOT_FOUND (or rate-limited)
10671066 logger . warn ( "Failed to update delayed disconnection event, prepare it again:" , e ) ;
10681067 this . disconnectDelayId = undefined ;
10691068 await prepareDelayedDisconnection ( ) ;
@@ -1076,13 +1075,13 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
10761075 let sentDelayedDisconnect = false ;
10771076 if ( this . disconnectDelayId !== undefined ) {
10781077 try {
1079- await this . client . _unstable_updateDelayedEvent (
1080- this . disconnectDelayId ,
1078+ const knownDisconnectDelayId = this . disconnectDelayId ;
1079+ await resendIfRateLimited ( ( ) => this . client . _unstable_updateDelayedEvent (
1080+ knownDisconnectDelayId ,
10811081 UpdateDelayedEventAction . Send ,
1082- ) ;
1082+ ) ) ;
10831083 sentDelayedDisconnect = true ;
10841084 } catch ( e ) {
1085- // TODO: Retry if rate-limited
10861085 logger . error ( "Failed to send our delayed disconnection event:" , e ) ;
10871086 }
10881087 this . disconnectDelayId = undefined ;
@@ -1111,10 +1110,10 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
11111110
11121111 private readonly delayDisconnection = async ( ) : Promise < void > => {
11131112 try {
1114- await this . client . _unstable_updateDelayedEvent ( this . disconnectDelayId ! , UpdateDelayedEventAction . Restart ) ;
1113+ const knownDisconnectDelayId = this . disconnectDelayId ! ;
1114+ await resendIfRateLimited ( ( ) => this . client . _unstable_updateDelayedEvent ( knownDisconnectDelayId , UpdateDelayedEventAction . Restart ) ) ;
11151115 this . scheduleDelayDisconnection ( ) ;
11161116 } catch ( e ) {
1117- // TODO: Retry if rate-limited
11181117 logger . error ( "Failed to delay our disconnection event:" , e ) ;
11191118 }
11201119 } ;
@@ -1162,3 +1161,27 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
11621161 this . sendEncryptionKeysEvent ( newKeyIndex ) ;
11631162 } ;
11641163}
1164+
1165+ async function resendIfRateLimited < T > ( func : ( ) => Promise < T > , numRetriesAllowed : number = 1 ) : Promise < T > {
1166+ while ( true ) {
1167+ try {
1168+ return await func ( ) ;
1169+ } catch ( e ) {
1170+ if ( numRetriesAllowed > 0 && e instanceof HTTPError && e . isRateLimitError ( ) ) {
1171+ numRetriesAllowed -- ;
1172+ let resendDelay : number ;
1173+ const defaultMs = 5000 ;
1174+ try {
1175+ resendDelay = e . getRetryAfterMs ( ) ?? defaultMs ;
1176+ logger . info ( `Rate limited by server, retrying in ${ resendDelay } ms` ) ;
1177+ } catch ( e ) {
1178+ logger . warn ( `Error while retrieving a rate-limit retry delay, retrying after default delay of ${ defaultMs } ` , e ) ;
1179+ resendDelay = defaultMs ;
1180+ }
1181+ await sleep ( resendDelay ) ;
1182+ } else {
1183+ throw e ;
1184+ }
1185+ }
1186+ } ;
1187+ }
0 commit comments