Skip to content

Commit 91b1fc3

Browse files
committed
added the stopRealtime and some more arguments in the header
1 parent 5f138c6 commit 91b1fc3

File tree

1 file changed

+74
-26
lines changed

1 file changed

+74
-26
lines changed

packages/remote-config/src/client/realtime_handler.ts

Lines changed: 74 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { ConfigUpdateObserver, FetchResponse } from '../public_types';
19-
const ORIGINAL_RETRIES = 8;
18+
import { ConfigUpdateObserver } from '../public_types';
2019
import { ERROR_FACTORY, ErrorCode } from '../errors';
2120
import { _FirebaseInstallationsInternal } from '@firebase/installations';
2221
import { Storage } from '../storage/storage';
2322
import { calculateBackoffMillis, FirebaseError } from '@firebase/util';
2423

24+
const ORIGINAL_RETRIES = 8;
25+
2526
export class RealtimeHandler {
2627
constructor(
2728
private readonly firebaseInstallations: _FirebaseInstallationsInternal,
@@ -45,9 +46,9 @@ export class RealtimeHandler {
4546
* Adds an observer to the realtime updates.
4647
* @param observer The observer to add.
4748
*/
48-
addObserver(observer: ConfigUpdateObserver): void {
49+
async addObserver(observer: ConfigUpdateObserver): Promise<void> {
4950
this.observers.add(observer);
50-
this.beginRealtime();
51+
await this.beginRealtime();
5152
}
5253

5354
/**
@@ -58,11 +59,14 @@ export class RealtimeHandler {
5859
if (this.observers.has(observer)) {
5960
this.observers.delete(observer);
6061
}
62+
if (this.observers.size === 0) {
63+
this.stopRealtime();
64+
}
6165
}
6266

63-
private beginRealtime(): void {
67+
private async beginRealtime(): Promise<void> {
6468
if (this.observers.size > 0) {
65-
this.makeRealtimeHttpConnection(0);
69+
await this.makeRealtimeHttpConnection(0);
6670
}
6771
}
6872

@@ -78,18 +82,17 @@ export class RealtimeHandler {
7882
return hasActiveListeners && isNotDisabled && isForeground && isNoConnectionActive;
7983
}
8084

81-
82-
private makeRealtimeHttpConnection(delayMillis: number): void {
85+
private async makeRealtimeHttpConnection(delayMillis: number): Promise<void> {
8386
if (this.scheduledConnectionTimeoutId) {
8487
clearTimeout(this.scheduledConnectionTimeoutId);
8588
}
8689
if (!this.canEstablishStreamConnection()) {
8790
return;
8891
}
89-
this.scheduledConnectionTimeoutId = setTimeout(() => {
92+
this.scheduledConnectionTimeoutId = setTimeout(async () => {
9093
if (this.retriesRemaining > 0) {
9194
this.retriesRemaining--;
92-
this.beginRealtimeHttpStream();
95+
await this.beginRealtimeHttpStream();
9396
} else if (!this.isInBackground) {
9497
const error = ERROR_FACTORY.create(ErrorCode.CONFIG_UPDATE_STREAM_ERROR, { originalErrorMessage: 'Unable to connect to the server. Check your connection and try again.' });
9598
this.propagateError(error);
@@ -110,6 +113,15 @@ export class RealtimeHandler {
110113
return false;
111114
}
112115

116+
private stopRealtime(): void {
117+
if (this.scheduledConnectionTimeoutId) {
118+
clearTimeout(this.scheduledConnectionTimeoutId);
119+
this.scheduledConnectionTimeoutId = undefined;
120+
}
121+
this.streamController?.abort();
122+
this.isConnectionActive = false;
123+
}
124+
113125
private resetRetryCount(): void {
114126
this.retriesRemaining = ORIGINAL_RETRIES;
115127
}
@@ -142,12 +154,13 @@ export class RealtimeHandler {
142154
await this.storage.setLastKnownTemplateVersion(0);
143155
}
144156

145-
const backoffEndTime = new Date(metadata.backoffEndTimeMillis).getTime();
157+
const backoffEndTime = metadata.backoffEndTimeMillis.getTime();
158+
146159
if (Date.now() < backoffEndTime) {
147-
this.retryHttpConnectionWhenBackoffEnds();
160+
await this.retryHttpConnectionWhenBackoffEnds();
148161
return;
149162
}
150-
let response;
163+
let response: Response | undefined;
151164
try {
152165
const [installationId, installationTokenResult] = await Promise.all([
153166
this.firebaseInstallations.getId(),
@@ -156,8 +169,11 @@ export class RealtimeHandler {
156169
const headers = {
157170
'Content-Type': 'application/json',
158171
'Content-Encoding': 'gzip',
172+
'X-Google-GFE-Can-Retry': 'yes',
173+
'X-Accept-Response-Streaming': 'true',
159174
'If-None-Match': '*',
160-
'authentication-token': installationTokenResult
175+
'authentication-token': installationTokenResult,
176+
'Accept': 'application/json'
161177
};
162178

163179
const url = this.getRealtimeUrl();
@@ -170,43 +186,43 @@ export class RealtimeHandler {
170186
appInstanceId: installationId
171187
};
172188

173-
response = await fetch(url, {
189+
response = await fetch(url, {
174190
method: "POST",
175191
headers,
176192
body: JSON.stringify(requestBody)
177193
});
178-
if (response.status === 200 && response.body) {
194+
if (response?.status === 200 && response?.body) {
179195
this.resetRetryCount();
180-
this.resetRealtimeBackoff();
196+
await this.resetRealtimeBackoff();
181197
//code related to start StartAutofetch
182-
//and then give the notification for al the observers
198+
//and then give the notification to all the observers
183199
} else {
184200
throw new FirebaseError('http-status-error', `HTTP Error: ${response.status}`);
185201
}
186-
} catch (error: any) {
202+
} catch (error) {
187203
if (this.isInBackground) {
188204
// It's possible the app was backgrounded while the connection was open, which
189205
// threw an exception trying to read the response. No real error here, so treat
190206
// this as a success, even if we haven't read a 200 response code yet.
191207
this.resetRetryCount();
208+
} else {
209+
console.error('Exception connecting to real-time RC backend. Retrying the connection...:', error);
192210
}
193211
} finally {
194212
this.isConnectionActive = false;
195213
const statusCode = response?.status;
196214
const connectionFailed = !this.isInBackground && (!statusCode || this.isStatusCodeRetryable(statusCode));
197215

198216
if (connectionFailed) {
199-
this.updateBackoffMetadataWithLastFailedStreamConnectionTime(new Date());
217+
await this.updateBackoffMetadataWithLastFailedStreamConnectionTime(new Date());
200218
}
201219

202220
if (connectionFailed || statusCode === 200) {
203-
this.retryHttpConnectionWhenBackoffEnds();
221+
await this.retryHttpConnectionWhenBackoffEnds();
204222
} else {
205-
//still have to implement this part
206223
let errorMessage = `Unable to connect to the server. Try again in a few minutes. HTTP status code: ${statusCode}`;
207-
if (statusCode === 403) {
208-
//still have to implemet this parseErrorResponseBody method
209-
// errorMessage = await this.parseErrorResponseBody(response?.body);
224+
if (statusCode === 403 && response) {
225+
errorMessage = await this.parseErrorResponseBody(response.body);
210226
}
211227
const firebaseError = ERROR_FACTORY.create(ErrorCode.CONFIG_UPDATE_STREAM_ERROR, {
212228
httpStatus: statusCode,
@@ -238,7 +254,7 @@ export class RealtimeHandler {
238254
const backoffEndTime = new Date(metadata.backoffEndTimeMillis).getTime();
239255
const currentTime = Date.now();
240256
const retrySeconds = Math.max(0, backoffEndTime - currentTime);
241-
this.makeRealtimeHttpConnection(retrySeconds);
257+
await this.makeRealtimeHttpConnection(retrySeconds);
242258
}
243259

244260
private async resetRealtimeBackoff(): Promise<void> {
@@ -248,6 +264,7 @@ export class RealtimeHandler {
248264
});
249265
}
250266

267+
251268
private getRealtimeUrl(): URL {
252269
const urlBase =
253270
window.FIREBASE_REMOTE_CONFIG_URL_BASE ||
@@ -257,4 +274,35 @@ export class RealtimeHandler {
257274
return new URL(urlString);
258275
}
259276

277+
private async parseErrorResponseBody(
278+
body: ReadableStream<Uint8Array> | null
279+
): Promise<string> {
280+
if (!body) {
281+
return 'Response body is empty.';
282+
}
283+
284+
try {
285+
const reader = body.getReader();
286+
const chunks: Uint8Array[] = [];
287+
while (true) {
288+
const { done, value } = await reader.read();
289+
if (done) {
290+
break;
291+
}
292+
chunks.push(value);
293+
}
294+
const blob = new Blob(chunks);
295+
const text = await blob.text();
296+
297+
const jsonResponse = JSON.parse(text);
298+
return (
299+
jsonResponse.error?.message ||
300+
jsonResponse.message ||
301+
'Unknown error from server.'
302+
);
303+
} catch (e) {
304+
return 'Could not parse error response body, or body is not JSON.';
305+
}
306+
}
260307
}
308+

0 commit comments

Comments
 (0)