1+ import { Mutex } from '@livekit/mutex' ;
12import type { RegionInfo , RegionSettings } from '@livekit/protocol' ;
23import log from '../logger' ;
34import { ConnectionError , ConnectionErrorReason } from './errors' ;
@@ -16,14 +17,47 @@ export class RegionUrlProvider {
1617
1718 private static settingsTimeouts : Map < string , ReturnType < typeof setTimeout > > = new Map ( ) ;
1819
19- private static scheduleRefetch ( url : URL , token : string , maxAgeInMs : number ) {
20- const timeout = RegionUrlProvider . settingsTimeouts . get ( url . host ) ;
20+ private static fetchLock = new Mutex ( ) ;
21+
22+ private static async fetchRegionSettings (
23+ serverUrl : URL ,
24+ token : string ,
25+ signal ?: AbortSignal ,
26+ ) : Promise < CachedRegionSettings > {
27+ const unlock = await RegionUrlProvider . fetchLock . lock ( ) ;
28+ try {
29+ const regionSettingsResponse = await fetch ( `${ getCloudConfigUrl ( serverUrl ) } /regions` , {
30+ headers : { authorization : `Bearer ${ token } ` } ,
31+ signal,
32+ } ) ;
33+ if ( regionSettingsResponse . ok ) {
34+ const maxAge = extractMaxAgeFromRequestHeaders ( regionSettingsResponse . headers ) ;
35+ const maxAgeInMs = maxAge ? maxAge * 1000 : DEFAULT_MAX_AGE_MS ;
36+
37+ const regionSettings = ( await regionSettingsResponse . json ( ) ) as RegionSettings ;
38+ return { regionSettings, updatedAtInMs : Date . now ( ) , maxAgeInMs } ;
39+ } else {
40+ throw new ConnectionError (
41+ `Could not fetch region settings: ${ regionSettingsResponse . statusText } ` ,
42+ regionSettingsResponse . status === 401
43+ ? ConnectionErrorReason . NotAllowed
44+ : ConnectionErrorReason . InternalError ,
45+ regionSettingsResponse . status ,
46+ ) ;
47+ }
48+ } finally {
49+ unlock ( ) ;
50+ }
51+ }
52+
53+ private static async scheduleRefetch ( url : URL , token : string , maxAgeInMs : number ) {
54+ const timeout = RegionUrlProvider . settingsTimeouts . get ( url . hostname ) ;
2155 clearTimeout ( timeout ) ;
2256 RegionUrlProvider . settingsTimeouts . set (
23- url . host ,
57+ url . hostname ,
2458 setTimeout ( async ( ) => {
2559 try {
26- const newSettings = await fetchRegionSettings ( url , token ) ;
60+ const newSettings = await RegionUrlProvider . fetchRegionSettings ( url , token ) ;
2761 RegionUrlProvider . updateCachedRegionSettings ( url , token , newSettings ) ;
2862 } catch ( error : unknown ) {
2963 log . debug ( 'auto refetching of region settings failed' , { error } ) ;
@@ -68,7 +102,7 @@ export class RegionUrlProvider {
68102
69103 /** @internal */
70104 async fetchRegionSettings ( abortSignal ?: AbortSignal ) : Promise < CachedRegionSettings > {
71- return fetchRegionSettings ( this . serverUrl , this . token , abortSignal ) ;
105+ return RegionUrlProvider . fetchRegionSettings ( this . serverUrl , this . token , abortSignal ) ;
72106 }
73107
74108 async getNextBestRegionUrl ( abortSignal ?: AbortSignal ) {
@@ -108,29 +142,3 @@ export class RegionUrlProvider {
108142function getCloudConfigUrl ( serverUrl : URL ) {
109143 return `${ serverUrl . protocol . replace ( 'ws' , 'http' ) } //${ serverUrl . host } /settings` ;
110144}
111-
112- async function fetchRegionSettings (
113- serverUrl : URL ,
114- token : string ,
115- signal ?: AbortSignal ,
116- ) : Promise < CachedRegionSettings > {
117- const regionSettingsResponse = await fetch ( `${ getCloudConfigUrl ( serverUrl ) } /regions` , {
118- headers : { authorization : `Bearer ${ token } ` } ,
119- signal,
120- } ) ;
121- if ( regionSettingsResponse . ok ) {
122- const maxAge = extractMaxAgeFromRequestHeaders ( regionSettingsResponse . headers ) ;
123- const maxAgeInMs = maxAge ? maxAge * 1000 : DEFAULT_MAX_AGE_MS ;
124-
125- const regionSettings = ( await regionSettingsResponse . json ( ) ) as RegionSettings ;
126- return { regionSettings, updatedAtInMs : Date . now ( ) , maxAgeInMs } ;
127- } else {
128- throw new ConnectionError (
129- `Could not fetch region settings: ${ regionSettingsResponse . statusText } ` ,
130- regionSettingsResponse . status === 401
131- ? ConnectionErrorReason . NotAllowed
132- : ConnectionErrorReason . InternalError ,
133- regionSettingsResponse . status ,
134- ) ;
135- }
136- }
0 commit comments