15
15
* limitations under the License.
16
16
*/
17
17
18
- import { _FirebaseInstallationsInternal } from " @firebase/installations" ;
19
- import { ConfigUpdateObserver } from " ../public_types" ;
20
- import { calculateBackoffMillis , FirebaseError } from " @firebase/util" ;
21
- import { ERROR_FACTORY , ErrorCode } from " ../errors" ;
22
- import { Storage } from " ../storage/storage" ;
18
+ import { _FirebaseInstallationsInternal } from ' @firebase/installations' ;
19
+ import { ConfigUpdateObserver } from ' ../public_types' ;
20
+ import { calculateBackoffMillis , FirebaseError } from ' @firebase/util' ;
21
+ import { ERROR_FACTORY , ErrorCode } from ' ../errors' ;
22
+ import { Storage } from ' ../storage/storage' ;
23
23
import { isBefore } from 'date-fns' ;
24
24
25
25
const API_KEY_HEADER = 'X-Goog-Api-Key' ;
@@ -36,66 +36,66 @@ export class RealtimeHandler {
36
36
private readonly namespace : string ,
37
37
private readonly projectId : string ,
38
38
private readonly apiKey : string ,
39
- private readonly appId : string ,
40
- ) {
41
- this . httpRetriesRemaining = ORIGINAL_RETRIES ;
39
+ private readonly appId : string
40
+ ) {
42
41
this . setRetriesRemaining ( ) ;
43
42
}
44
43
45
- private observers : Set < ConfigUpdateObserver > = new Set < ConfigUpdateObserver > ( ) ;
44
+ private observers : Set < ConfigUpdateObserver > =
45
+ new Set < ConfigUpdateObserver > ( ) ;
46
46
private isConnectionActive : boolean = false ;
47
47
private isRealtimeDisabled : boolean = false ;
48
48
private controller ?: AbortController ;
49
49
private reader : ReadableStreamDefaultReader | undefined ;
50
50
private httpRetriesRemaining : number = ORIGINAL_RETRIES ;
51
51
52
52
private async setRetriesRemaining ( ) {
53
- // Retrieve number of remaining retries from last session. The minimum retry count being one.
54
- const metadata = await this . storage . getRealtimeBackoffMetadata ( ) ;
55
- const numFailedStreams = metadata ?. numFailedStreams || 0 ;
56
- this . httpRetriesRemaining = Math . max ( ORIGINAL_RETRIES - numFailedStreams , 1 ) ;
57
- }
58
-
59
- /**
60
- * Removes an observer from the realtime updates.
61
- * @param observer The observer to remove.
62
- */
63
- removeObserver ( observer : ConfigUpdateObserver ) : void {
64
- if ( this . observers . has ( observer ) ) {
65
- this . observers . delete ( observer ) ;
66
- }
53
+ // Retrieve number of remaining retries from last session. The minimum retry count being one.
54
+ const metadata = await this . storage . getRealtimeBackoffMetadata ( ) ;
55
+ const numFailedStreams = metadata ?. numFailedStreams || 0 ;
56
+ this . httpRetriesRemaining = Math . max (
57
+ ORIGINAL_RETRIES - numFailedStreams ,
58
+ 1
59
+ ) ;
67
60
}
68
61
69
- private propagateError = ( e : FirebaseError ) => this . observers . forEach ( o => o . error ?.( e ) ) ;
62
+ private propagateError = ( e : FirebaseError ) =>
63
+ this . observers . forEach ( o => o . error ?.( e ) ) ;
70
64
71
65
/**
72
66
* Increment the number of failed stream attempts, increase the backoff duration, set the backoff
73
67
* end time to "backoff duration" after {@code lastFailedStreamTime} and persist the new
74
68
* values to storage metadata.
75
69
*/
76
- private async updateBackoffMetadataWithLastFailedStreamConnectionTime ( lastFailedStreamTime : Date ) : Promise < void > {
77
- const numFailedStreams = ( ( await this . storage . getRealtimeBackoffMetadata ( ) ) ?. numFailedStreams || 0 ) + 1 ;
70
+ private async updateBackoffMetadataWithLastFailedStreamConnectionTime (
71
+ lastFailedStreamTime : Date
72
+ ) : Promise < void > {
73
+ const numFailedStreams =
74
+ ( ( await this . storage . getRealtimeBackoffMetadata ( ) ) ?. numFailedStreams ||
75
+ 0 ) + 1 ;
78
76
const backoffMillis = calculateBackoffMillis ( numFailedStreams ) ;
79
77
await this . storage . setRealtimeBackoffMetadata ( {
80
- backoffEndTimeMillis : new Date ( lastFailedStreamTime . getTime ( ) + backoffMillis ) ,
78
+ backoffEndTimeMillis : new Date (
79
+ lastFailedStreamTime . getTime ( ) + backoffMillis
80
+ ) ,
81
81
numFailedStreams
82
82
} ) ;
83
83
}
84
-
84
+
85
85
/**
86
86
* HTTP status code that the Realtime client should retry on.
87
87
*/
88
88
private isStatusCodeRetryable = ( statusCode ?: number ) : boolean => {
89
89
const retryableStatusCodes = [
90
- 408 , // Request Timeout
91
- 429 , // Too Many Requests
92
- 502 , // Bad Gateway
93
- 503 , // Service Unavailable
94
- 504 // Gateway Timeout
90
+ 408 , // Request Timeout
91
+ 429 , // Too Many Requests
92
+ 502 , // Bad Gateway
93
+ 503 , // Service Unavailable
94
+ 504 // Gateway Timeout
95
95
] ;
96
96
return ! statusCode || retryableStatusCodes . includes ( statusCode ) ;
97
- }
98
-
97
+ } ;
98
+
99
99
/**
100
100
* Stops the real-time HTTP connection by aborting the in-progress fetch request
101
101
* and canceling the stream reader if they exist.
@@ -111,34 +111,40 @@ export class RealtimeHandler {
111
111
this . reader = undefined ;
112
112
}
113
113
}
114
-
114
+
115
115
private async resetRealtimeBackoff ( ) : Promise < void > {
116
116
await this . storage . setRealtimeBackoffMetadata ( {
117
117
backoffEndTimeMillis : new Date ( - 1 ) ,
118
118
numFailedStreams : 0
119
119
} ) ;
120
- }
121
-
120
+ }
121
+
122
122
private resetRetryCount ( ) : void {
123
123
this . httpRetriesRemaining = ORIGINAL_RETRIES ;
124
124
}
125
-
125
+
126
126
/**
127
127
* Assembles the request headers and body and executes the fetch request to
128
128
* establish the real-time streaming connection. This is the "worker" method
129
129
* that performs the actual network communication.
130
- */
131
- private async establishRealtimeConnection ( url : URL , installationId : string , installationTokenResult : string , signal : AbortSignal ) : Promise < Response > {
130
+ */
131
+ private async establishRealtimeConnection (
132
+ url : URL ,
133
+ installationId : string ,
134
+ installationTokenResult : string ,
135
+ signal : AbortSignal
136
+ ) : Promise < Response > {
132
137
const eTagValue = await this . storage . getActiveConfigEtag ( ) ;
133
- const lastKnownVersionNumber = await this . storage . getLastKnownTemplateVersion ( ) ;
134
-
138
+ const lastKnownVersionNumber =
139
+ await this . storage . getLastKnownTemplateVersion ( ) ;
140
+
135
141
const headers = {
136
142
[ API_KEY_HEADER ] : this . apiKey ,
137
143
[ INSTALLATIONS_AUTH_TOKEN_HEADER ] : installationTokenResult ,
138
144
'Content-Type' : 'application/json' ,
139
145
'Accept' : 'application/json' ,
140
146
'If-None-Match' : eTagValue || '*' ,
141
- 'Content-Encoding' : 'gzip' ,
147
+ 'Content-Encoding' : 'gzip'
142
148
} ;
143
149
144
150
const requestBody = {
@@ -175,7 +181,12 @@ export class RealtimeHandler {
175
181
] ) ;
176
182
this . controller = new AbortController ( ) ;
177
183
const url = this . getRealtimeUrl ( ) ;
178
- return await this . establishRealtimeConnection ( url , installationId , installationTokenResult , this . controller . signal ) ;
184
+ return await this . establishRealtimeConnection (
185
+ url ,
186
+ installationId ,
187
+ installationTokenResult ,
188
+ this . controller . signal
189
+ ) ;
179
190
}
180
191
181
192
/**
@@ -187,9 +198,11 @@ export class RealtimeHandler {
187
198
backoffMetadata = {
188
199
backoffEndTimeMillis : new Date ( NO_BACKOFF_TIME_IN_MILLIS ) ,
189
200
numFailedStreams : NO_FAILED_REALTIME_STREAMS
190
- }
191
- }
192
- const backoffEndTime = new Date ( backoffMetadata . backoffEndTimeMillis ) . getTime ( ) ;
201
+ } ;
202
+ }
203
+ const backoffEndTime = new Date (
204
+ backoffMetadata . backoffEndTimeMillis
205
+ ) . getTime ( ) ;
193
206
const currentTime = Date . now ( ) ;
194
207
const retryMillis = Math . max ( 0 , backoffEndTime - currentTime ) ;
195
208
this . makeRealtimeHttpConnection ( retryMillis ) ;
@@ -218,12 +231,13 @@ export class RealtimeHandler {
218
231
if ( ! this . checkAndSetHttpConnectionFlagIfNotRunning ( ) ) {
219
232
return ;
220
233
}
234
+
221
235
let backoffMetadata = await this . storage . getRealtimeBackoffMetadata ( ) ;
222
236
if ( ! backoffMetadata ) {
223
237
backoffMetadata = {
224
238
backoffEndTimeMillis : new Date ( NO_BACKOFF_TIME_IN_MILLIS ) ,
225
239
numFailedStreams : NO_FAILED_REALTIME_STREAMS
226
- }
240
+ } ;
227
241
}
228
242
const backoffEndTime = backoffMetadata . backoffEndTimeMillis . getTime ( ) ;
229
243
if ( isBefore ( new Date ( ) , backoffEndTime ) ) {
@@ -234,47 +248,56 @@ export class RealtimeHandler {
234
248
let response : Response | undefined ;
235
249
let responseCode : number | undefined ;
236
250
try {
237
- //this has been called in the try cause it throws an error if the method does not get implemented
238
- response = await this . createRealtimeConnection ( ) ;
239
- responseCode = response . status ;
240
- if ( response . ok && response . body ) {
241
- this . resetRetryCount ( ) ;
242
- await this . resetRealtimeBackoff ( ) ;
243
- //const configAutoFetch = this.startAutoFetch(reader);
244
- //await configAutoFetch.listenForNotifications();
245
- }
251
+ //this has been called in the try cause it throws an error if the method does not get implemented
252
+ response = await this . createRealtimeConnection ( ) ;
253
+ responseCode = response . status ;
254
+ if ( response . ok && response . body ) {
255
+ this . resetRetryCount ( ) ;
256
+ await this . resetRealtimeBackoff ( ) ;
257
+ //const configAutoFetch = this.startAutoFetch(reader);
258
+ //await configAutoFetch.listenForNotifications();
259
+ }
246
260
} catch ( error ) {
247
261
//there might have been a transient error so the client will retry the connection.
248
- console . error ( 'Exception connecting to real-time RC backend. Retrying the connection...:' , error ) ;
262
+ console . error (
263
+ 'Exception connecting to real-time RC backend. Retrying the connection...:' ,
264
+ error
265
+ ) ;
249
266
} finally {
250
267
// Close HTTP connection and associated streams.
251
268
this . closeRealtimeHttpConnection ( ) ;
252
269
this . setIsHttpConnectionRunning ( false ) ;
253
-
270
+
254
271
// Update backoff metadata if the connection failed in the foreground.
255
- const connectionFailed = responseCode == null || this . isStatusCodeRetryable ( responseCode ) ;
272
+ const connectionFailed =
273
+ responseCode == null || this . isStatusCodeRetryable ( responseCode ) ;
256
274
257
275
if ( connectionFailed ) {
258
- await this . updateBackoffMetadataWithLastFailedStreamConnectionTime ( new Date ( ) ) ;
276
+ await this . updateBackoffMetadataWithLastFailedStreamConnectionTime (
277
+ new Date ( )
278
+ ) ;
259
279
}
260
280
// If responseCode is null then no connection was made to server and the SDK should still retry.
261
- if ( connectionFailed || response ?. ok ) {
281
+ if ( connectionFailed || response ?. ok ) {
262
282
await this . retryHttpConnectionWhenBackoffEnds ( ) ;
263
283
} else {
264
284
let errorMessage = `Unable to connect to the server. HTTP status code: ${ responseCode } ` ;
265
- const firebaseError = ERROR_FACTORY . create ( ErrorCode . CONFIG_UPDATE_STREAM_ERROR , {
266
- httpStatus : responseCode ,
267
- originalErrorMessage : errorMessage
268
- } ) ;
285
+ const firebaseError = ERROR_FACTORY . create (
286
+ ErrorCode . CONFIG_UPDATE_STREAM_ERROR ,
287
+ {
288
+ httpStatus : responseCode ,
289
+ originalErrorMessage : errorMessage
290
+ }
291
+ ) ;
269
292
this . propagateError ( firebaseError ) ;
270
293
}
271
294
}
272
295
}
273
296
274
297
/**
275
- * Checks whether connection can be made or not based on some conditions
276
- * @returns booelean
277
- */
298
+ * Checks whether connection can be made or not based on some conditions
299
+ * @returns booelean
300
+ */
278
301
private canEstablishStreamConnection ( ) : boolean {
279
302
const hasActiveListeners = this . observers . size > 0 ;
280
303
const isNotDisabled = ! this . isRealtimeDisabled ;
@@ -301,11 +324,21 @@ export class RealtimeHandler {
301
324
}
302
325
303
326
/**
304
- * Adds an observer to the realtime updates.
305
- * @param observer The observer to add.
306
- */
327
+ * Adds an observer to the realtime updates.
328
+ * @param observer The observer to add.
329
+ */
307
330
async addObserver ( observer : ConfigUpdateObserver ) : Promise < void > {
308
331
this . observers . add ( observer ) ;
309
332
await this . beginRealtime ( ) ;
310
333
}
334
+
335
+ /**
336
+ * Removes an observer from the realtime updates.
337
+ * @param observer The observer to remove.
338
+ */
339
+ removeObserver ( observer : ConfigUpdateObserver ) : void {
340
+ if ( this . observers . has ( observer ) ) {
341
+ this . observers . delete ( observer ) ;
342
+ }
343
+ }
311
344
}
0 commit comments