@cloudbase/realtime 1.2.3-alpha.0 → 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +15 -0
- package/dist/cjs/common.js +1 -1
- package/dist/cjs/error.js +17 -16
- package/dist/cjs/index.js +5 -5
- package/dist/cjs/listener.js +2 -4
- package/dist/cjs/message.js +2 -2
- package/dist/cjs/snapshot.js +7 -7
- package/dist/cjs/utils.js +3 -2
- package/dist/cjs/virtual-websocket-client.js +86 -83
- package/dist/cjs/websocket-client.js +162 -176
- package/dist/cjs/ws-event.js +27 -26
- package/dist/esm/common.js +1 -1
- package/dist/esm/error.js +13 -15
- package/dist/esm/index.js +3 -3
- package/dist/esm/listener.js +2 -4
- package/dist/esm/message.js +2 -2
- package/dist/esm/snapshot.js +7 -7
- package/dist/esm/utils.js +1 -1
- package/dist/esm/virtual-websocket-client.js +75 -72
- package/dist/esm/websocket-client.js +156 -170
- package/dist/esm/ws-event.js +25 -25
- package/package.json +6 -22
- package/src/common.ts +5 -5
- package/src/error.ts +34 -40
- package/src/index.ts +15 -15
- package/src/listener.ts +11 -13
- package/src/message.ts +5 -7
- package/src/snapshot.ts +20 -20
- package/src/utils.ts +1 -1
- package/src/virtual-websocket-client.ts +446 -514
- package/src/websocket-client.ts +493 -518
- package/src/ws-event.ts +28 -28
- package/tsconfig.esm.json +2 -20
- package/tsconfig.json +2 -20
- package/.eslintrc +0 -30
package/src/websocket-client.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/* eslint-disable no-plusplus */
|
|
2
|
+
/* eslint-disable @typescript-eslint/consistent-type-assertions */
|
|
3
|
+
/* eslint-disable new-cap */
|
|
4
|
+
/* eslint-disable @typescript-eslint/no-misused-promises */
|
|
5
|
+
import { VirtualWebSocketClient } from './virtual-websocket-client';
|
|
6
|
+
import { genRequestId } from './message';
|
|
3
7
|
import {
|
|
4
8
|
IDatabaseServiceContext,
|
|
5
|
-
} from '@cloudbase/types/database'
|
|
9
|
+
} from '@cloudbase/types/database';
|
|
6
10
|
import {
|
|
7
11
|
IWatchOptions,
|
|
8
12
|
DBRealtimeListener,
|
|
@@ -11,17 +15,17 @@ import {
|
|
|
11
15
|
IRequestMessagePingMsg,
|
|
12
16
|
IRequestMessageLoginMsg,
|
|
13
17
|
IResponseMessageLoginResMsg,
|
|
14
|
-
IRequestMessageLoginData
|
|
15
|
-
} from '@cloudbase/types/realtime'
|
|
18
|
+
IRequestMessageLoginData,
|
|
19
|
+
} from '@cloudbase/types/realtime';
|
|
16
20
|
import {
|
|
17
21
|
CLOSE_EVENT_CODE,
|
|
18
22
|
CLOSE_EVENT_CODE_INFO,
|
|
19
|
-
getWSCloseError
|
|
20
|
-
} from './ws-event'
|
|
23
|
+
getWSCloseError,
|
|
24
|
+
} from './ws-event';
|
|
21
25
|
|
|
22
|
-
import { ERR_CODE, TimeoutError, RealtimeErrorMessageError,CloudSDKError } from './error'
|
|
23
|
-
import { getWsClass, getRuntime } from './common'
|
|
24
|
-
import { sleep } from './utils'
|
|
26
|
+
import { ERR_CODE, TimeoutError, RealtimeErrorMessageError, CloudSDKError } from './error';
|
|
27
|
+
import { getWsClass, getRuntime } from './common';
|
|
28
|
+
import { sleep } from './utils';
|
|
25
29
|
|
|
26
30
|
export interface IRealtimeWebSocketClientConstructorOptions {
|
|
27
31
|
maxReconnect?: number
|
|
@@ -85,215 +89,203 @@ const WS_READY_STATE = {
|
|
|
85
89
|
CONNECTING: 0,
|
|
86
90
|
OPEN: 1,
|
|
87
91
|
CLOSING: 2,
|
|
88
|
-
CLOSED: 3
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const MAX_RTT_OBSERVED = 3
|
|
92
|
-
const DEFAULT_EXPECTED_EVENT_WAIT_TIME = 5000
|
|
93
|
-
const DEFAULT_UNTRUSTED_RTT_THRESHOLD = 10000
|
|
94
|
-
const DEFAULT_MAX_RECONNECT = 5
|
|
95
|
-
const DEFAULT_WS_RECONNECT_INTERVAL = 10000
|
|
92
|
+
CLOSED: 3,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const MAX_RTT_OBSERVED = 3;
|
|
96
|
+
const DEFAULT_EXPECTED_EVENT_WAIT_TIME = 5000;
|
|
97
|
+
const DEFAULT_UNTRUSTED_RTT_THRESHOLD = 10000;
|
|
98
|
+
const DEFAULT_MAX_RECONNECT = 5;
|
|
99
|
+
const DEFAULT_WS_RECONNECT_INTERVAL = 10000;
|
|
96
100
|
// const DEFAULT_WS_RECONNECT_MAX_VALID_INTERVAL = 3 * 60 * 1000
|
|
97
|
-
const DEFAULT_PING_FAIL_TOLERANCE = 2
|
|
98
|
-
const DEFAULT_PONG_MISS_TOLERANCE = 2
|
|
99
|
-
const DEFAULT_LOGIN_TIMEOUT = 5000
|
|
101
|
+
const DEFAULT_PING_FAIL_TOLERANCE = 2;
|
|
102
|
+
const DEFAULT_PONG_MISS_TOLERANCE = 2;
|
|
103
|
+
const DEFAULT_LOGIN_TIMEOUT = 5000;
|
|
100
104
|
|
|
101
105
|
export class RealtimeWebSocketClient {
|
|
102
|
-
private _virtualWSClient: Set<VirtualWebSocketClient> = new Set()
|
|
106
|
+
private _virtualWSClient: Set<VirtualWebSocketClient> = new Set();
|
|
103
107
|
// after listener initWatch, the listener has the queryID and can store it here
|
|
104
|
-
private _queryIdClientMap: Map<string, VirtualWebSocketClient> = new Map()
|
|
105
|
-
private _watchIdClientMap: Map<string, VirtualWebSocketClient> = new Map()
|
|
106
|
-
private _maxReconnect: number
|
|
108
|
+
private _queryIdClientMap: Map<string, VirtualWebSocketClient> = new Map();
|
|
109
|
+
private _watchIdClientMap: Map<string, VirtualWebSocketClient> = new Map();
|
|
110
|
+
private _maxReconnect: number;
|
|
107
111
|
// private _availableRetries: number
|
|
108
|
-
private _reconnectInterval: number
|
|
109
|
-
private _context: IDatabaseServiceContext
|
|
112
|
+
private _reconnectInterval: number;
|
|
113
|
+
private _context: IDatabaseServiceContext;
|
|
110
114
|
// private _ws?: WXNS.Socket.ISocketTask
|
|
111
|
-
private _ws?: any
|
|
112
|
-
private _lastPingSendTS?: number
|
|
113
|
-
private _pingFailed = 0
|
|
114
|
-
private _pongMissed = 0
|
|
115
|
-
private _pingTimeoutId?: number
|
|
116
|
-
private _pongTimeoutId?: number
|
|
117
|
-
private _logins: Map<string /* envId */, ILoginInfo> = new Map()
|
|
115
|
+
private _ws?: any;
|
|
116
|
+
private _lastPingSendTS?: number;
|
|
117
|
+
private _pingFailed = 0;
|
|
118
|
+
private _pongMissed = 0;
|
|
119
|
+
private _pingTimeoutId?: number;
|
|
120
|
+
private _pongTimeoutId?: number;
|
|
121
|
+
private _logins: Map<string /* envId */, ILoginInfo> = new Map();
|
|
118
122
|
// private _loginInfo: ILoginInfo
|
|
119
123
|
// private _signatures: Map<string /* envId */, ISignature> = new Map()
|
|
120
|
-
private _wsInitPromise?: Promise<void
|
|
121
|
-
private _wsReadySubsribers: IResolveReject[] = []
|
|
124
|
+
private _wsInitPromise?: Promise<void>;
|
|
125
|
+
private _wsReadySubsribers: IResolveReject[] = [];
|
|
122
126
|
private _wsResponseWait: Map<
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
> = new Map()
|
|
126
|
-
private _rttObserved: number[] = []
|
|
127
|
-
private _reconnectState: boolean
|
|
127
|
+
string /* requestId */,
|
|
128
|
+
IResponseWaitSpec
|
|
129
|
+
> = new Map();
|
|
130
|
+
private _rttObserved: number[] = [];
|
|
131
|
+
private _reconnectState: boolean;
|
|
128
132
|
// obtained from the first getSignature with no envId provided
|
|
129
133
|
// private _defaultEnvId?: string
|
|
130
|
-
private _wsSign: IWsSign
|
|
134
|
+
private _wsSign: IWsSign;
|
|
131
135
|
|
|
132
136
|
constructor(options: IRealtimeWebSocketClientConstructorOptions) {
|
|
133
|
-
this._maxReconnect = options.maxReconnect || DEFAULT_MAX_RECONNECT
|
|
137
|
+
this._maxReconnect = options.maxReconnect || DEFAULT_MAX_RECONNECT;
|
|
134
138
|
// this._availableRetries = this._maxReconnect
|
|
135
|
-
this._reconnectInterval =
|
|
136
|
-
|
|
137
|
-
this._context = options.context
|
|
139
|
+
this._reconnectInterval = options.reconnectInterval || DEFAULT_WS_RECONNECT_INTERVAL;
|
|
140
|
+
this._context = options.context;
|
|
138
141
|
}
|
|
139
142
|
|
|
140
143
|
clearHeartbeat() {
|
|
141
|
-
this._pingTimeoutId && clearTimeout(this._pingTimeoutId)
|
|
142
|
-
this._pongTimeoutId && clearTimeout(this._pongTimeoutId)
|
|
144
|
+
this._pingTimeoutId && clearTimeout(this._pingTimeoutId);
|
|
145
|
+
this._pongTimeoutId && clearTimeout(this._pongTimeoutId);
|
|
143
146
|
}
|
|
144
147
|
|
|
145
|
-
send = async <T = any>(opts: IWSSendOptions): Promise<T> =>
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
)
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
148
|
+
send = async <T = any>(opts: IWSSendOptions): Promise<T> => new Promise<T>(async (_resolve, _reject) => {
|
|
149
|
+
let timeoutId: number;
|
|
150
|
+
let _hasResolved = false;
|
|
151
|
+
let _hasRejected = false;
|
|
152
|
+
|
|
153
|
+
const resolve: typeof _resolve = (value?: T | PromiseLike<T> | undefined) => {
|
|
154
|
+
_hasResolved = true;
|
|
155
|
+
timeoutId && clearTimeout(timeoutId);
|
|
156
|
+
_resolve(value);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const reject: typeof _reject = (error: any) => {
|
|
160
|
+
_hasRejected = true;
|
|
161
|
+
timeoutId && clearTimeout(timeoutId);
|
|
162
|
+
_reject(error);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
if (opts.timeout) {
|
|
166
|
+
// @ts-ignore
|
|
167
|
+
timeoutId = setTimeout(async () => {
|
|
168
|
+
if (!_hasResolved || !_hasRejected) {
|
|
169
|
+
// wait another immediate timeout to allow the success/fail callback to be invoked if ws has already got the result,
|
|
170
|
+
// this is because the timer is registered before ws.send
|
|
171
|
+
await sleep(0);
|
|
168
172
|
if (!_hasResolved || !_hasRejected) {
|
|
169
|
-
|
|
170
|
-
// this is because the timer is registered before ws.send
|
|
171
|
-
await sleep(0)
|
|
172
|
-
if (!_hasResolved || !_hasRejected) {
|
|
173
|
-
reject(new TimeoutError('wsclient.send timedout'))
|
|
174
|
-
}
|
|
173
|
+
reject(new TimeoutError('wsclient.send timedout'));
|
|
175
174
|
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
175
|
+
}
|
|
176
|
+
}, opts.timeout);
|
|
177
|
+
}
|
|
178
178
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
179
|
+
try {
|
|
180
|
+
// if (this._context.debug) {
|
|
181
|
+
// console.log(`[realtime] ws send (${new Date()}): `, opts)
|
|
182
|
+
// console.log(
|
|
183
|
+
// `[realtime] ws send ${
|
|
184
|
+
// opts.msg.msgType
|
|
185
|
+
// } (${new Date().toLocaleString()}): `,
|
|
186
|
+
// opts
|
|
187
|
+
// )
|
|
188
|
+
// }
|
|
189
|
+
|
|
190
|
+
if (this._wsInitPromise) {
|
|
191
|
+
await this._wsInitPromise;
|
|
192
|
+
}
|
|
189
193
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
194
|
+
if (!this._ws) {
|
|
195
|
+
reject(new Error('invalid state: ws connection not exists, can not send message'));
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
193
198
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
)
|
|
199
|
-
)
|
|
200
|
-
return
|
|
201
|
-
}
|
|
199
|
+
if (this._ws.readyState !== WS_READY_STATE.OPEN) {
|
|
200
|
+
reject(new Error(`ws readyState invalid: ${this._ws.readyState}, can not send message`));
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
202
203
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
204
|
+
if (opts.waitResponse) {
|
|
205
|
+
this._wsResponseWait.set(opts.msg.requestId, {
|
|
206
|
+
resolve,
|
|
207
|
+
reject,
|
|
208
|
+
skipOnMessage: opts.skipOnMessage,
|
|
209
|
+
} as IResponseWaitSpec);
|
|
210
|
+
}
|
|
211
211
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
} as IResponseWaitSpec)
|
|
212
|
+
// console.log('send msg:', opts.msg)
|
|
213
|
+
try {
|
|
214
|
+
await this._ws.send(JSON.stringify(opts.msg));
|
|
215
|
+
if (!opts.waitResponse) {
|
|
216
|
+
resolve(undefined);
|
|
218
217
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
resolve()
|
|
225
|
-
}
|
|
226
|
-
} catch (err) {
|
|
227
|
-
if (err) {
|
|
228
|
-
reject(err)
|
|
229
|
-
if (opts.waitResponse) {
|
|
230
|
-
this._wsResponseWait.delete(opts.msg.requestId)
|
|
231
|
-
}
|
|
218
|
+
} catch (err) {
|
|
219
|
+
if (err) {
|
|
220
|
+
reject(err);
|
|
221
|
+
if (opts.waitResponse) {
|
|
222
|
+
this._wsResponseWait.delete(opts.msg.requestId);
|
|
232
223
|
}
|
|
233
224
|
}
|
|
234
|
-
// this._ws.send(JSON.stringify(opts.msg), err => {
|
|
235
|
-
// if (err) {
|
|
236
|
-
// reject(err)
|
|
237
|
-
// if (opts.waitResponse) {
|
|
238
|
-
// this._wsResponseWait.delete(opts.msg.requestId)
|
|
239
|
-
// }
|
|
240
|
-
// return
|
|
241
|
-
// }
|
|
242
|
-
|
|
243
|
-
// if (!opts.waitResponse) {
|
|
244
|
-
// resolve()
|
|
245
|
-
// }
|
|
246
|
-
// })
|
|
247
|
-
|
|
248
|
-
// this._ws.send({
|
|
249
|
-
// data: JSON.stringify(opts.msg),
|
|
250
|
-
// success: res => {
|
|
251
|
-
// if (!opts.waitResponse) {
|
|
252
|
-
// resolve(res)
|
|
253
|
-
// }
|
|
254
|
-
// },
|
|
255
|
-
// fail: e => {
|
|
256
|
-
// reject(e)
|
|
257
|
-
// if (opts.waitResponse) {
|
|
258
|
-
// this._wsResponseWait.delete(opts.msg.requestId)
|
|
259
|
-
// }
|
|
260
|
-
// }
|
|
261
|
-
// })
|
|
262
|
-
} catch (e) {
|
|
263
|
-
reject(e)
|
|
264
225
|
}
|
|
265
|
-
|
|
226
|
+
// this._ws.send(JSON.stringify(opts.msg), err => {
|
|
227
|
+
// if (err) {
|
|
228
|
+
// reject(err)
|
|
229
|
+
// if (opts.waitResponse) {
|
|
230
|
+
// this._wsResponseWait.delete(opts.msg.requestId)
|
|
231
|
+
// }
|
|
232
|
+
// return
|
|
233
|
+
// }
|
|
234
|
+
|
|
235
|
+
// if (!opts.waitResponse) {
|
|
236
|
+
// resolve()
|
|
237
|
+
// }
|
|
238
|
+
// })
|
|
239
|
+
|
|
240
|
+
// this._ws.send({
|
|
241
|
+
// data: JSON.stringify(opts.msg),
|
|
242
|
+
// success: res => {
|
|
243
|
+
// if (!opts.waitResponse) {
|
|
244
|
+
// resolve(res)
|
|
245
|
+
// }
|
|
246
|
+
// },
|
|
247
|
+
// fail: e => {
|
|
248
|
+
// reject(e)
|
|
249
|
+
// if (opts.waitResponse) {
|
|
250
|
+
// this._wsResponseWait.delete(opts.msg.requestId)
|
|
251
|
+
// }
|
|
252
|
+
// }
|
|
253
|
+
// })
|
|
254
|
+
} catch (e) {
|
|
255
|
+
reject(e);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
266
258
|
|
|
267
259
|
close(code: CLOSE_EVENT_CODE) {
|
|
268
|
-
this.clearHeartbeat()
|
|
260
|
+
this.clearHeartbeat();
|
|
269
261
|
|
|
270
262
|
if (this._ws) {
|
|
271
|
-
this._ws.close(code, CLOSE_EVENT_CODE_INFO[code].name)
|
|
272
|
-
this._ws = undefined
|
|
263
|
+
this._ws.close(code, CLOSE_EVENT_CODE_INFO[code].name);
|
|
264
|
+
this._ws = undefined;
|
|
273
265
|
}
|
|
274
266
|
}
|
|
275
267
|
|
|
276
268
|
closeAllClients = (error: any) => {
|
|
277
|
-
this._virtualWSClient.forEach(client => {
|
|
278
|
-
client.closeWithError(error)
|
|
279
|
-
})
|
|
280
|
-
}
|
|
269
|
+
this._virtualWSClient.forEach((client) => {
|
|
270
|
+
client.closeWithError(error);
|
|
271
|
+
});
|
|
272
|
+
};
|
|
281
273
|
|
|
282
274
|
pauseClients = (clients?: Set<VirtualWebSocketClient>) => {
|
|
283
|
-
|
|
284
|
-
client.pause()
|
|
285
|
-
})
|
|
286
|
-
}
|
|
275
|
+
(clients || this._virtualWSClient).forEach((client) => {
|
|
276
|
+
client.pause();
|
|
277
|
+
});
|
|
278
|
+
};
|
|
287
279
|
|
|
288
280
|
resumeClients = (clients?: Set<VirtualWebSocketClient>) => {
|
|
289
|
-
|
|
290
|
-
client.resume()
|
|
291
|
-
})
|
|
292
|
-
}
|
|
281
|
+
(clients || this._virtualWSClient).forEach((client) => {
|
|
282
|
+
client.resume();
|
|
283
|
+
});
|
|
284
|
+
};
|
|
293
285
|
|
|
294
286
|
watch(options: IWSWatchOptions): DBRealtimeListener {
|
|
295
287
|
if (!this._ws && !this._wsInitPromise) {
|
|
296
|
-
this.initWebSocketConnection(false)
|
|
288
|
+
this.initWebSocketConnection(false);
|
|
297
289
|
}
|
|
298
290
|
|
|
299
291
|
const virtualClient = new VirtualWebSocketClient({
|
|
@@ -305,11 +297,11 @@ export class RealtimeWebSocketClient {
|
|
|
305
297
|
getWaitExpectedTimeoutLength: this.getWaitExpectedTimeoutLength,
|
|
306
298
|
onWatchStart: this.onWatchStart,
|
|
307
299
|
onWatchClose: this.onWatchClose,
|
|
308
|
-
debug: true
|
|
309
|
-
})
|
|
310
|
-
this._virtualWSClient.add(virtualClient)
|
|
311
|
-
this._watchIdClientMap.set(virtualClient.watchId, virtualClient)
|
|
312
|
-
return virtualClient.listener
|
|
300
|
+
debug: true,
|
|
301
|
+
});
|
|
302
|
+
this._virtualWSClient.add(virtualClient);
|
|
303
|
+
this._watchIdClientMap.set(virtualClient.watchId, virtualClient);
|
|
304
|
+
return virtualClient.listener;
|
|
313
305
|
}
|
|
314
306
|
|
|
315
307
|
private initWebSocketConnection = async (
|
|
@@ -318,16 +310,16 @@ export class RealtimeWebSocketClient {
|
|
|
318
310
|
): Promise<void> => {
|
|
319
311
|
// 当前处于正在重连中的状态
|
|
320
312
|
if (reconnect && this._reconnectState) {
|
|
321
|
-
return // 忽略
|
|
313
|
+
return; // 忽略
|
|
322
314
|
}
|
|
323
315
|
|
|
324
316
|
if (reconnect) {
|
|
325
|
-
this._reconnectState = true // 重连状态开始
|
|
317
|
+
this._reconnectState = true; // 重连状态开始
|
|
326
318
|
}
|
|
327
319
|
|
|
328
320
|
if (this._wsInitPromise) {
|
|
329
321
|
// there already exists a websocket initiation, just wait for it
|
|
330
|
-
return this._wsInitPromise
|
|
322
|
+
return this._wsInitPromise;
|
|
331
323
|
}
|
|
332
324
|
|
|
333
325
|
// if (process.env.DEBUG) {
|
|
@@ -337,10 +329,10 @@ export class RealtimeWebSocketClient {
|
|
|
337
329
|
// }
|
|
338
330
|
|
|
339
331
|
if (reconnect) {
|
|
340
|
-
this.pauseClients()
|
|
332
|
+
this.pauseClients();
|
|
341
333
|
}
|
|
342
334
|
|
|
343
|
-
this.close(CLOSE_EVENT_CODE.ReconnectWebSocket)
|
|
335
|
+
this.close(CLOSE_EVENT_CODE.ReconnectWebSocket);
|
|
344
336
|
|
|
345
337
|
this._wsInitPromise = new Promise<void>(async (resolve, reject) => {
|
|
346
338
|
try {
|
|
@@ -358,14 +350,14 @@ export class RealtimeWebSocketClient {
|
|
|
358
350
|
// }
|
|
359
351
|
|
|
360
352
|
// const signature = await this.getSignature()
|
|
361
|
-
const wsSign = await this.getWsSign()
|
|
353
|
+
const wsSign = await this.getWsSign();
|
|
362
354
|
|
|
363
355
|
// if (process.env.DEBUG) {
|
|
364
356
|
// console.log('[realtime] initWebSocketConnection getSignature success')
|
|
365
357
|
// console.log('[realtime] initWebSocketConnection start connectSocket')
|
|
366
358
|
// }
|
|
367
359
|
|
|
368
|
-
await new Promise(success => {
|
|
360
|
+
await new Promise((success) => {
|
|
369
361
|
// this._ws = getSDK(this._context.identifiers)
|
|
370
362
|
// ._socketSkipCheckDomainFactory()
|
|
371
363
|
// .connectSocket({
|
|
@@ -379,12 +371,12 @@ export class RealtimeWebSocketClient {
|
|
|
379
371
|
|
|
380
372
|
const url = wsSign.wsUrl || 'wss://tcb-ws.tencentcloudapi.com';
|
|
381
373
|
const wsClass = getWsClass();
|
|
382
|
-
this._ws = wsClass ? new wsClass(url) : new WebSocket(url)
|
|
383
|
-
success()
|
|
384
|
-
})
|
|
374
|
+
this._ws = wsClass ? new wsClass(url) : new WebSocket(url);
|
|
375
|
+
success(undefined);
|
|
376
|
+
});
|
|
385
377
|
|
|
386
|
-
if(this._ws.connect){
|
|
387
|
-
await this._ws.connect()
|
|
378
|
+
if (this._ws.connect) {
|
|
379
|
+
await this._ws.connect();
|
|
388
380
|
}
|
|
389
381
|
|
|
390
382
|
// if (process.env.DEBUG) {
|
|
@@ -393,23 +385,23 @@ export class RealtimeWebSocketClient {
|
|
|
393
385
|
// )
|
|
394
386
|
// }
|
|
395
387
|
|
|
396
|
-
await this.initWebSocketEvent()
|
|
397
|
-
resolve()
|
|
388
|
+
await this.initWebSocketEvent();
|
|
389
|
+
resolve();
|
|
398
390
|
|
|
399
391
|
if (reconnect) {
|
|
400
|
-
this.resumeClients()
|
|
401
|
-
this._reconnectState = false // 重连状态结束
|
|
392
|
+
this.resumeClients();
|
|
393
|
+
this._reconnectState = false; // 重连状态结束
|
|
402
394
|
}
|
|
403
395
|
} catch (e) {
|
|
404
396
|
// if (process.env.DEBUG) {
|
|
405
|
-
console.error('[realtime] initWebSocketConnection connect fail', e)
|
|
397
|
+
console.error('[realtime] initWebSocketConnection connect fail', e);
|
|
406
398
|
// }
|
|
407
399
|
|
|
408
400
|
if (availableRetries > 0) {
|
|
409
401
|
// this is an optimization, in case of network offline, we don't need to stubbornly sleep for sometime,
|
|
410
402
|
// we only need to wait for the network to be back online, this ensures minimum downtime
|
|
411
403
|
// const { isConnected } = await getNetworkStatus()
|
|
412
|
-
const isConnected = true
|
|
404
|
+
const isConnected = true;
|
|
413
405
|
|
|
414
406
|
// if (process.env.DEBUG) {
|
|
415
407
|
// console.log(
|
|
@@ -430,7 +422,7 @@ export class RealtimeWebSocketClient {
|
|
|
430
422
|
// console.log('[realtime] initWebSocketConnection network online')
|
|
431
423
|
// }
|
|
432
424
|
|
|
433
|
-
this._wsInitPromise = undefined
|
|
425
|
+
this._wsInitPromise = undefined;
|
|
434
426
|
|
|
435
427
|
if (isConnected) {
|
|
436
428
|
// if (process.env.DEBUG) {
|
|
@@ -438,39 +430,37 @@ export class RealtimeWebSocketClient {
|
|
|
438
430
|
// `[realtime] initWebSocketConnection sleep ${this._reconnectInterval}ms`
|
|
439
431
|
// )
|
|
440
432
|
// }
|
|
441
|
-
await sleep(this._reconnectInterval)
|
|
433
|
+
await sleep(this._reconnectInterval);
|
|
442
434
|
if (reconnect) {
|
|
443
|
-
this._reconnectState = false // 重连异常也算重连状态结束
|
|
435
|
+
this._reconnectState = false; // 重连异常也算重连状态结束
|
|
444
436
|
}
|
|
445
437
|
}
|
|
446
438
|
|
|
447
|
-
resolve(this.initWebSocketConnection(reconnect, availableRetries - 1))
|
|
439
|
+
resolve(this.initWebSocketConnection(reconnect, availableRetries - 1));
|
|
448
440
|
} else {
|
|
449
|
-
reject(e)
|
|
441
|
+
reject(e);
|
|
450
442
|
|
|
451
443
|
if (reconnect) {
|
|
452
|
-
this.closeAllClients(
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
})
|
|
457
|
-
)
|
|
444
|
+
this.closeAllClients(new CloudSDKError({
|
|
445
|
+
errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_RECONNECT_WATCH_FAIL as string,
|
|
446
|
+
errMsg: e,
|
|
447
|
+
}));
|
|
458
448
|
}
|
|
459
449
|
}
|
|
460
450
|
}
|
|
461
|
-
})
|
|
451
|
+
});
|
|
462
452
|
|
|
463
453
|
// let success = false
|
|
464
454
|
|
|
465
455
|
try {
|
|
466
|
-
await this._wsInitPromise
|
|
456
|
+
await this._wsInitPromise;
|
|
467
457
|
// success = true
|
|
468
|
-
this._wsReadySubsribers.forEach(({ resolve }) => resolve())
|
|
458
|
+
this._wsReadySubsribers.forEach(({ resolve }) => resolve());
|
|
469
459
|
} catch (e) {
|
|
470
|
-
this._wsReadySubsribers.forEach(({ reject }) => reject())
|
|
460
|
+
this._wsReadySubsribers.forEach(({ reject }) => reject());
|
|
471
461
|
} finally {
|
|
472
|
-
this._wsInitPromise = undefined
|
|
473
|
-
this._wsReadySubsribers = []
|
|
462
|
+
this._wsInitPromise = undefined;
|
|
463
|
+
this._wsReadySubsribers = [];
|
|
474
464
|
}
|
|
475
465
|
|
|
476
466
|
// if (process.env.DEBUG) {
|
|
@@ -478,264 +468,255 @@ export class RealtimeWebSocketClient {
|
|
|
478
468
|
// `[realtime] initWebSocketConnection ${success ? 'success' : 'fail'}`
|
|
479
469
|
// )
|
|
480
470
|
// }
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
private initWebSocketEvent = () =>
|
|
484
|
-
new Promise<void>((resolve, reject) => {
|
|
485
|
-
if (!this._ws) {
|
|
486
|
-
throw new Error('can not initWebSocketEvent, ws not exists')
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
let wsOpened = false
|
|
490
|
-
|
|
491
|
-
this._ws.onopen = event => {
|
|
492
|
-
// this._ws.onOpen(() => {
|
|
493
|
-
// this._ws.on("open", () => {
|
|
494
|
-
// this._context.debug &&
|
|
495
|
-
console.warn('[realtime] ws event: open', event)
|
|
496
|
-
wsOpened = true
|
|
497
|
-
resolve()
|
|
498
|
-
}
|
|
471
|
+
};
|
|
499
472
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
this._logins = new Map()
|
|
473
|
+
private initWebSocketEvent = () => new Promise<void>((resolve, reject) => {
|
|
474
|
+
if (!this._ws) {
|
|
475
|
+
throw new Error('can not initWebSocketEvent, ws not exists');
|
|
476
|
+
}
|
|
505
477
|
|
|
506
|
-
|
|
478
|
+
let wsOpened = false;
|
|
507
479
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
// )
|
|
480
|
+
this._ws.onopen = (event) => {
|
|
481
|
+
// this._ws.onOpen(() => {
|
|
482
|
+
// this._ws.on("open", () => {
|
|
483
|
+
// this._context.debug &&
|
|
484
|
+
console.warn('[realtime] ws event: open', event);
|
|
485
|
+
wsOpened = true;
|
|
486
|
+
resolve();
|
|
487
|
+
};
|
|
517
488
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
489
|
+
this._ws.onerror = (event) => {
|
|
490
|
+
// this._ws.on("error", error => {
|
|
491
|
+
// this._ws.onError(error => {
|
|
492
|
+
// all logins are invalid after disconnection
|
|
493
|
+
this._logins = new Map();
|
|
522
494
|
|
|
523
|
-
|
|
524
|
-
// "wserror.txt",
|
|
525
|
-
// `${this.specialNumber} [realtime] ws event: error ${error} \n`
|
|
526
|
-
// )
|
|
527
|
-
|
|
528
|
-
this.clearHeartbeat()
|
|
529
|
-
this._virtualWSClient.forEach(client =>
|
|
530
|
-
client.closeWithError(
|
|
531
|
-
new CloudSDKError({
|
|
532
|
-
errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_WEBSOCKET_CONNECTION_ERROR as string,
|
|
533
|
-
errMsg: event
|
|
534
|
-
})
|
|
535
|
-
)
|
|
536
|
-
)
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
// TODO: reconnect
|
|
541
|
-
this._ws.onclose = closeEvent => {
|
|
542
|
-
// this._ws.on("close", (closeEvent, closereason) => {
|
|
543
|
-
// this._ws.onClose(closeEvent => {
|
|
544
|
-
// if (process.env.DEBUG) {
|
|
545
|
-
console.warn('[realtime] ws event: close', closeEvent)
|
|
546
|
-
// }
|
|
495
|
+
// error写进file
|
|
547
496
|
|
|
497
|
+
if (!wsOpened) {
|
|
498
|
+
// this._context.debug &&
|
|
499
|
+
console.error('[realtime] ws open failed with ws event: error', event);
|
|
548
500
|
// writeToFile(
|
|
549
|
-
// "
|
|
501
|
+
// "wserror.txt",
|
|
550
502
|
// `${
|
|
551
503
|
// this.specialNumber
|
|
552
|
-
// } [realtime] ws event:
|
|
504
|
+
// } [realtime] ws open failed with ws event: error ${error} \n`
|
|
553
505
|
// )
|
|
554
506
|
|
|
555
|
-
|
|
556
|
-
|
|
507
|
+
reject(event);
|
|
508
|
+
} else {
|
|
509
|
+
// this._context.debug &&
|
|
510
|
+
console.error('[realtime] ws event: error', event);
|
|
557
511
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
512
|
+
// writeToFile(
|
|
513
|
+
// "wserror.txt",
|
|
514
|
+
// `${this.specialNumber} [realtime] ws event: error ${error} \n`
|
|
515
|
+
// )
|
|
516
|
+
|
|
517
|
+
this.clearHeartbeat();
|
|
518
|
+
this._virtualWSClient.forEach(client => client.closeWithError(new CloudSDKError({
|
|
519
|
+
errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_WEBSOCKET_CONNECTION_ERROR as string,
|
|
520
|
+
errMsg: event,
|
|
521
|
+
})));
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
// TODO: reconnect
|
|
526
|
+
this._ws.onclose = (closeEvent) => {
|
|
527
|
+
// this._ws.on("close", (closeEvent, closereason) => {
|
|
528
|
+
// this._ws.onClose(closeEvent => {
|
|
529
|
+
// if (process.env.DEBUG) {
|
|
530
|
+
console.warn('[realtime] ws event: close', closeEvent);
|
|
531
|
+
// }
|
|
532
|
+
|
|
533
|
+
// writeToFile(
|
|
534
|
+
// "wsclose.txt",
|
|
535
|
+
// `${
|
|
536
|
+
// this.specialNumber
|
|
537
|
+
// } [realtime] ws event: close ${closeEvent} ${closereason} \n`
|
|
538
|
+
// )
|
|
539
|
+
|
|
540
|
+
// all logins are invalid after disconnection
|
|
541
|
+
this._logins = new Map();
|
|
542
|
+
|
|
543
|
+
this.clearHeartbeat();
|
|
544
|
+
switch (closeEvent.code) {
|
|
545
|
+
case CLOSE_EVENT_CODE.ReconnectWebSocket: {
|
|
546
|
+
// just ignore
|
|
547
|
+
break;
|
|
548
|
+
}
|
|
549
|
+
case CLOSE_EVENT_CODE.NoRealtimeListeners: {
|
|
550
|
+
// quit
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
553
|
+
case CLOSE_EVENT_CODE.HeartbeatPingError:
|
|
554
|
+
case CLOSE_EVENT_CODE.HeartbeatPongTimeoutError:
|
|
555
|
+
case CLOSE_EVENT_CODE.NormalClosure:
|
|
556
|
+
case CLOSE_EVENT_CODE.AbnormalClosure: {
|
|
557
|
+
// Normal Closure and Abnormal Closure:
|
|
558
|
+
// expected closure, most likely dispatched by wechat client,
|
|
559
|
+
// since this is the status code dispatched in case of network failure,
|
|
560
|
+
// we should retry
|
|
561
|
+
|
|
562
|
+
if (this._maxReconnect > 0) {
|
|
563
|
+
// if (this._availableRetries > 0) {
|
|
564
|
+
this.initWebSocketConnection(true, this._maxReconnect);
|
|
565
|
+
} else {
|
|
566
|
+
this.closeAllClients(getWSCloseError(closeEvent.code));
|
|
590
567
|
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
// this.
|
|
568
|
+
break;
|
|
569
|
+
}
|
|
570
|
+
case CLOSE_EVENT_CODE.NoAuthentication: {
|
|
571
|
+
this.closeAllClients(getWSCloseError(closeEvent.code, closeEvent.reason));
|
|
572
|
+
break;
|
|
573
|
+
}
|
|
574
|
+
default: {
|
|
575
|
+
// we should retry by default
|
|
576
|
+
if (this._maxReconnect > 0) {
|
|
577
|
+
// if (this._availableRetries > 0) {
|
|
578
|
+
this.initWebSocketConnection(true, this._maxReconnect);
|
|
579
|
+
} else {
|
|
580
|
+
this.closeAllClients(getWSCloseError(closeEvent.code));
|
|
601
581
|
}
|
|
582
|
+
// console.warn(`[realtime] unrecognize ws close event`, closeEvent)
|
|
583
|
+
// this.closeAllClients(getWSCloseError(closeEvent.code))
|
|
602
584
|
}
|
|
603
585
|
}
|
|
586
|
+
};
|
|
604
587
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
588
|
+
this._ws.onmessage = (res) => {
|
|
589
|
+
// this._ws.on("message", res => {
|
|
590
|
+
// this._ws.onMessage(res => {
|
|
591
|
+
// const rawMsg = res.data
|
|
592
|
+
const rawMsg = res.data;
|
|
610
593
|
|
|
611
|
-
|
|
612
|
-
|
|
594
|
+
// reset & restart heartbeat
|
|
595
|
+
this.heartbeat();
|
|
613
596
|
|
|
614
|
-
|
|
597
|
+
let msg: IResponseMessage;
|
|
615
598
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
599
|
+
try {
|
|
600
|
+
msg = JSON.parse(rawMsg as string);
|
|
601
|
+
} catch (e) {
|
|
602
|
+
throw new Error(`[realtime] onMessage parse res.data error: ${e}`);
|
|
603
|
+
}
|
|
621
604
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
605
|
+
// console.log(
|
|
606
|
+
// `[realtime] onMessage ${
|
|
607
|
+
// msg.msgType
|
|
608
|
+
// } (${new Date().toLocaleString()})`,
|
|
609
|
+
// msg
|
|
610
|
+
// )
|
|
611
|
+
|
|
612
|
+
if (msg.msgType === 'ERROR') {
|
|
613
|
+
// 找到当前监听,并将error返回
|
|
614
|
+
let virtualWatch = null;
|
|
615
|
+
this._virtualWSClient.forEach((item) => {
|
|
616
|
+
if (item.watchId === msg.watchId) {
|
|
617
|
+
virtualWatch = item;
|
|
618
|
+
}
|
|
619
|
+
});
|
|
628
620
|
|
|
629
|
-
if (
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
if (item.watchId === msg.watchId) {
|
|
634
|
-
virtualWatch = item
|
|
635
|
-
}
|
|
636
|
-
})
|
|
621
|
+
if (virtualWatch) {
|
|
622
|
+
virtualWatch.listener.onError(msg);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
637
625
|
|
|
638
|
-
|
|
639
|
-
|
|
626
|
+
const responseWaitSpec = this._wsResponseWait.get(msg.requestId);
|
|
627
|
+
if (responseWaitSpec) {
|
|
628
|
+
try {
|
|
629
|
+
if (msg.msgType === 'ERROR') {
|
|
630
|
+
responseWaitSpec.reject(new RealtimeErrorMessageError(msg));
|
|
631
|
+
} else {
|
|
632
|
+
responseWaitSpec.resolve(msg);
|
|
640
633
|
}
|
|
634
|
+
} catch (e) {
|
|
635
|
+
// this._context.debug &&
|
|
636
|
+
console.error(
|
|
637
|
+
'ws onMessage responseWaitSpec.resolve(msg) errored:',
|
|
638
|
+
e
|
|
639
|
+
);
|
|
640
|
+
} finally {
|
|
641
|
+
this._wsResponseWait.delete(msg.requestId);
|
|
642
|
+
}
|
|
643
|
+
if (responseWaitSpec.skipOnMessage) {
|
|
644
|
+
return;
|
|
641
645
|
}
|
|
646
|
+
}
|
|
642
647
|
|
|
643
|
-
|
|
644
|
-
if (
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
responseWaitSpec.reject(new RealtimeErrorMessageError(msg))
|
|
648
|
-
} else {
|
|
649
|
-
responseWaitSpec.resolve(msg)
|
|
650
|
-
}
|
|
651
|
-
} catch (e) {
|
|
648
|
+
if (msg.msgType === 'PONG') {
|
|
649
|
+
if (this._lastPingSendTS) {
|
|
650
|
+
const rtt = Date.now() - this._lastPingSendTS;
|
|
651
|
+
if (rtt > DEFAULT_UNTRUSTED_RTT_THRESHOLD) {
|
|
652
652
|
// this._context.debug &&
|
|
653
|
-
console.
|
|
654
|
-
|
|
655
|
-
e
|
|
656
|
-
)
|
|
657
|
-
} finally {
|
|
658
|
-
this._wsResponseWait.delete(msg.requestId)
|
|
653
|
+
console.warn(`[realtime] untrusted rtt observed: ${rtt}`);
|
|
654
|
+
return;
|
|
659
655
|
}
|
|
660
|
-
if (
|
|
661
|
-
|
|
656
|
+
if (this._rttObserved.length >= MAX_RTT_OBSERVED) {
|
|
657
|
+
this._rttObserved.splice(
|
|
658
|
+
0,
|
|
659
|
+
this._rttObserved.length - MAX_RTT_OBSERVED + 1
|
|
660
|
+
);
|
|
662
661
|
}
|
|
662
|
+
this._rttObserved.push(rtt);
|
|
663
663
|
}
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
664
666
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
667
|
+
let client = msg.watchId && this._watchIdClientMap.get(msg.watchId);
|
|
668
|
+
if (client) {
|
|
669
|
+
client.onMessage(msg);
|
|
670
|
+
} else {
|
|
671
|
+
// TODO, this is a temporary fix done for server
|
|
672
|
+
// if (process.env.DEBUG) {
|
|
673
|
+
console.error(
|
|
674
|
+
`[realtime] no realtime listener found responsible for watchId ${msg.watchId}: `,
|
|
675
|
+
msg
|
|
676
|
+
);
|
|
677
|
+
// }
|
|
678
|
+
switch (msg.msgType) {
|
|
679
|
+
case 'INIT_EVENT':
|
|
680
|
+
case 'NEXT_EVENT':
|
|
681
|
+
case 'CHECK_EVENT': {
|
|
682
|
+
client = this._queryIdClientMap.get(msg.msgData.queryID);
|
|
683
|
+
if (client) {
|
|
684
|
+
client.onMessage(msg);
|
|
678
685
|
}
|
|
679
|
-
|
|
686
|
+
break;
|
|
680
687
|
}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
client.onMessage(msg)
|
|
687
|
-
} else {
|
|
688
|
-
// TODO, this is a temporary fix done for server
|
|
689
|
-
// if (process.env.DEBUG) {
|
|
690
|
-
console.error(
|
|
691
|
-
`[realtime] no realtime listener found responsible for watchId ${msg.watchId}: `,
|
|
692
|
-
msg
|
|
693
|
-
)
|
|
694
|
-
// }
|
|
695
|
-
switch (msg.msgType) {
|
|
696
|
-
case 'INIT_EVENT':
|
|
697
|
-
case 'NEXT_EVENT':
|
|
698
|
-
case 'CHECK_EVENT': {
|
|
699
|
-
client = this._queryIdClientMap.get(msg.msgData.queryID)
|
|
700
|
-
if (client) {
|
|
701
|
-
client.onMessage(msg)
|
|
702
|
-
}
|
|
703
|
-
break
|
|
704
|
-
}
|
|
705
|
-
default: {
|
|
706
|
-
for (const [,client] of Array.from(this._watchIdClientMap.entries())) {
|
|
707
|
-
// console.log('watchid*****', watchId)
|
|
708
|
-
client.onMessage(msg)
|
|
709
|
-
break
|
|
710
|
-
}
|
|
688
|
+
default: {
|
|
689
|
+
for (const [,client] of Array.from(this._watchIdClientMap.entries())) {
|
|
690
|
+
// console.log('watchid*****', watchId)
|
|
691
|
+
client.onMessage(msg);
|
|
692
|
+
break;
|
|
711
693
|
}
|
|
712
694
|
}
|
|
713
695
|
}
|
|
714
696
|
}
|
|
697
|
+
};
|
|
715
698
|
|
|
716
|
-
|
|
717
|
-
|
|
699
|
+
this.heartbeat();
|
|
700
|
+
});
|
|
718
701
|
|
|
719
|
-
private isWSConnected = (): boolean =>
|
|
720
|
-
return Boolean(this._ws && this._ws.readyState === WS_READY_STATE.OPEN)
|
|
721
|
-
}
|
|
702
|
+
private isWSConnected = (): boolean => Boolean(this._ws && this._ws.readyState === WS_READY_STATE.OPEN);
|
|
722
703
|
|
|
723
704
|
private onceWSConnected = async (): Promise<void> => {
|
|
724
705
|
if (this.isWSConnected()) {
|
|
725
|
-
return
|
|
706
|
+
return;
|
|
726
707
|
}
|
|
727
708
|
|
|
728
709
|
if (this._wsInitPromise) {
|
|
729
|
-
return this._wsInitPromise
|
|
710
|
+
return this._wsInitPromise;
|
|
730
711
|
}
|
|
731
712
|
|
|
732
713
|
return new Promise<void>((resolve, reject) => {
|
|
733
714
|
this._wsReadySubsribers.push({
|
|
734
715
|
resolve,
|
|
735
|
-
reject
|
|
736
|
-
})
|
|
737
|
-
})
|
|
738
|
-
}
|
|
716
|
+
reject,
|
|
717
|
+
});
|
|
718
|
+
});
|
|
719
|
+
};
|
|
739
720
|
|
|
740
721
|
private webLogin = async (
|
|
741
722
|
envId?: string,
|
|
@@ -744,21 +725,21 @@ export class RealtimeWebSocketClient {
|
|
|
744
725
|
if (!refresh) {
|
|
745
726
|
// let loginInfo = this._loginInfo
|
|
746
727
|
if (envId) {
|
|
747
|
-
const loginInfo = this._logins.get(envId)
|
|
728
|
+
const loginInfo = this._logins.get(envId);
|
|
748
729
|
if (loginInfo) {
|
|
749
730
|
if (loginInfo.loggedIn && loginInfo.loginResult) {
|
|
750
731
|
// if (process.env.DEBUG) {
|
|
751
732
|
// console.log('[realtime] login: already logged in')
|
|
752
733
|
// }
|
|
753
|
-
return loginInfo.loginResult
|
|
754
|
-
}
|
|
755
|
-
return loginInfo.loggingInPromise
|
|
734
|
+
return loginInfo.loginResult;
|
|
735
|
+
} if (loginInfo.loggingInPromise) {
|
|
736
|
+
return loginInfo.loggingInPromise;
|
|
756
737
|
}
|
|
757
738
|
}
|
|
758
739
|
} else {
|
|
759
|
-
const emptyEnvLoginInfo = this._logins.get('')
|
|
740
|
+
const emptyEnvLoginInfo = this._logins.get('');
|
|
760
741
|
if (emptyEnvLoginInfo?.loggingInPromise) {
|
|
761
|
-
return emptyEnvLoginInfo.loggingInPromise
|
|
742
|
+
return emptyEnvLoginInfo.loggingInPromise;
|
|
762
743
|
}
|
|
763
744
|
}
|
|
764
745
|
}
|
|
@@ -768,7 +749,7 @@ export class RealtimeWebSocketClient {
|
|
|
768
749
|
try {
|
|
769
750
|
// const signature = await this.getSignature(envId, refresh)
|
|
770
751
|
|
|
771
|
-
const wsSign = await this.getWsSign()
|
|
752
|
+
const wsSign = await this.getWsSign();
|
|
772
753
|
|
|
773
754
|
// const wxVersion = getWXVersion()
|
|
774
755
|
const msgData: IRequestMessageLoginData = {
|
|
@@ -778,8 +759,8 @@ export class RealtimeWebSocketClient {
|
|
|
778
759
|
// secretVersion: signature.secretVersion,
|
|
779
760
|
referrer: 'web',
|
|
780
761
|
sdkVersion: '',
|
|
781
|
-
dataVersion: ''
|
|
782
|
-
}
|
|
762
|
+
dataVersion: '',
|
|
763
|
+
};
|
|
783
764
|
const loginMsg: IRequestMessageLoginMsg = {
|
|
784
765
|
watchId: undefined,
|
|
785
766
|
requestId: genRequestId(),
|
|
@@ -788,51 +769,47 @@ export class RealtimeWebSocketClient {
|
|
|
788
769
|
exMsgData: {
|
|
789
770
|
runtime: getRuntime(),
|
|
790
771
|
signStr: wsSign.signStr,
|
|
791
|
-
secretVersion: wsSign.secretVersion
|
|
792
|
-
}
|
|
793
|
-
}
|
|
772
|
+
secretVersion: wsSign.secretVersion,
|
|
773
|
+
},
|
|
774
|
+
};
|
|
794
775
|
const loginResMsg = await this.send<IResponseMessageLoginResMsg>({
|
|
795
776
|
msg: loginMsg,
|
|
796
777
|
waitResponse: true,
|
|
797
778
|
skipOnMessage: true,
|
|
798
|
-
timeout: DEFAULT_LOGIN_TIMEOUT
|
|
799
|
-
})
|
|
779
|
+
timeout: DEFAULT_LOGIN_TIMEOUT,
|
|
780
|
+
});
|
|
800
781
|
|
|
801
782
|
if (!loginResMsg.msgData.code) {
|
|
802
783
|
// login success
|
|
803
784
|
resolve({
|
|
804
|
-
envId: wsSign.envId
|
|
805
|
-
})
|
|
785
|
+
envId: wsSign.envId,
|
|
786
|
+
});
|
|
806
787
|
} else {
|
|
807
788
|
// login failed
|
|
808
|
-
reject(
|
|
809
|
-
new Error(
|
|
810
|
-
`${loginResMsg.msgData.code} ${loginResMsg.msgData.message}`
|
|
811
|
-
)
|
|
812
|
-
)
|
|
789
|
+
reject(new Error(`${loginResMsg.msgData.code} ${loginResMsg.msgData.message}`));
|
|
813
790
|
}
|
|
814
791
|
} catch (e) {
|
|
815
|
-
reject(e)
|
|
792
|
+
reject(e);
|
|
816
793
|
}
|
|
817
|
-
})
|
|
794
|
+
});
|
|
818
795
|
|
|
819
796
|
// let loginInfo = this._loginInfo
|
|
820
|
-
let loginInfo = envId && this._logins.get(envId)
|
|
797
|
+
let loginInfo = envId && this._logins.get(envId);
|
|
821
798
|
|
|
822
|
-
const loginStartTS = Date.now()
|
|
799
|
+
const loginStartTS = Date.now();
|
|
823
800
|
|
|
824
801
|
if (loginInfo) {
|
|
825
|
-
loginInfo.loggedIn = false
|
|
826
|
-
loginInfo.loggingInPromise = promise
|
|
827
|
-
loginInfo.loginStartTS = loginStartTS
|
|
802
|
+
loginInfo.loggedIn = false;
|
|
803
|
+
loginInfo.loggingInPromise = promise;
|
|
804
|
+
loginInfo.loginStartTS = loginStartTS;
|
|
828
805
|
} else {
|
|
829
806
|
loginInfo = {
|
|
830
807
|
loggedIn: false,
|
|
831
808
|
loggingInPromise: promise,
|
|
832
|
-
loginStartTS
|
|
833
|
-
}
|
|
809
|
+
loginStartTS,
|
|
810
|
+
};
|
|
834
811
|
// this._loginInfo = loginInfo
|
|
835
|
-
this._logins.set(envId || '', loginInfo)
|
|
812
|
+
this._logins.set(envId || '', loginInfo);
|
|
836
813
|
}
|
|
837
814
|
|
|
838
815
|
// try {
|
|
@@ -851,113 +828,111 @@ export class RealtimeWebSocketClient {
|
|
|
851
828
|
// }
|
|
852
829
|
|
|
853
830
|
try {
|
|
854
|
-
const loginResult = await promise
|
|
855
|
-
const curLoginInfo = envId && this._logins.get(envId)
|
|
831
|
+
const loginResult = await promise;
|
|
832
|
+
const curLoginInfo = envId && this._logins.get(envId);
|
|
856
833
|
if (
|
|
857
|
-
curLoginInfo
|
|
858
|
-
curLoginInfo === loginInfo
|
|
859
|
-
curLoginInfo.loginStartTS === loginStartTS
|
|
834
|
+
curLoginInfo
|
|
835
|
+
&& curLoginInfo === loginInfo
|
|
836
|
+
&& curLoginInfo.loginStartTS === loginStartTS
|
|
860
837
|
) {
|
|
861
|
-
loginInfo.loggedIn = true
|
|
862
|
-
loginInfo.loggingInPromise = undefined
|
|
863
|
-
loginInfo.loginStartTS = undefined
|
|
864
|
-
loginInfo.loginResult = loginResult
|
|
865
|
-
return loginResult
|
|
866
|
-
}
|
|
838
|
+
loginInfo.loggedIn = true;
|
|
839
|
+
loginInfo.loggingInPromise = undefined;
|
|
840
|
+
loginInfo.loginStartTS = undefined;
|
|
841
|
+
loginInfo.loginResult = loginResult;
|
|
842
|
+
return loginResult;
|
|
843
|
+
} if (curLoginInfo) {
|
|
867
844
|
if (curLoginInfo.loggedIn && curLoginInfo.loginResult) {
|
|
868
|
-
return curLoginInfo.loginResult
|
|
869
|
-
}
|
|
870
|
-
return curLoginInfo.loggingInPromise
|
|
871
|
-
} else {
|
|
872
|
-
throw new Error('ws unexpected login info')
|
|
845
|
+
return curLoginInfo.loginResult;
|
|
846
|
+
} if (curLoginInfo.loggingInPromise) {
|
|
847
|
+
return curLoginInfo.loggingInPromise;
|
|
873
848
|
}
|
|
849
|
+
throw new Error('ws unexpected login info');
|
|
874
850
|
} else {
|
|
875
|
-
throw new Error('ws login info reset')
|
|
851
|
+
throw new Error('ws login info reset');
|
|
876
852
|
}
|
|
877
853
|
} catch (e) {
|
|
878
|
-
loginInfo.loggedIn = false
|
|
879
|
-
loginInfo.loggingInPromise = undefined
|
|
880
|
-
loginInfo.loginStartTS = undefined
|
|
881
|
-
loginInfo.loginResult = undefined
|
|
882
|
-
throw e
|
|
854
|
+
loginInfo.loggedIn = false;
|
|
855
|
+
loginInfo.loggingInPromise = undefined;
|
|
856
|
+
loginInfo.loginStartTS = undefined;
|
|
857
|
+
loginInfo.loginResult = undefined;
|
|
858
|
+
throw e;
|
|
883
859
|
}
|
|
884
|
-
}
|
|
860
|
+
};
|
|
885
861
|
|
|
886
862
|
private getWsSign = async (): Promise<IWsSign> => {
|
|
887
863
|
if (this._wsSign && this._wsSign.expiredTs > Date.now()) {
|
|
888
|
-
return this._wsSign
|
|
864
|
+
return this._wsSign;
|
|
889
865
|
}
|
|
890
|
-
const expiredTs = Date.now() + 60000
|
|
891
|
-
const res = await this._context.appConfig.request.send('auth.wsWebSign', {runtime: getRuntime()})
|
|
866
|
+
const expiredTs = Date.now() + 60000;
|
|
867
|
+
const res = await this._context.appConfig.request.send('auth.wsWebSign', { runtime: getRuntime() });
|
|
892
868
|
|
|
893
869
|
if (res.code) {
|
|
894
|
-
throw new Error(`[tcb-js-sdk] 获取实时数据推送登录票据失败: ${res.code}`)
|
|
870
|
+
throw new Error(`[tcb-js-sdk] 获取实时数据推送登录票据失败: ${res.code}`);
|
|
895
871
|
}
|
|
896
872
|
|
|
897
873
|
if (res.data) {
|
|
898
|
-
const {signStr, wsUrl, secretVersion, envId} = res.data
|
|
874
|
+
const { signStr, wsUrl, secretVersion, envId } = res.data;
|
|
899
875
|
return {
|
|
900
876
|
signStr,
|
|
901
877
|
wsUrl,
|
|
902
878
|
secretVersion,
|
|
903
879
|
envId,
|
|
904
|
-
expiredTs
|
|
905
|
-
}
|
|
906
|
-
} else {
|
|
907
|
-
throw new Error('[tcb-js-sdk] 获取实时数据推送登录票据失败')
|
|
880
|
+
expiredTs,
|
|
881
|
+
};
|
|
908
882
|
}
|
|
909
|
-
|
|
883
|
+
throw new Error('[tcb-js-sdk] 获取实时数据推送登录票据失败');
|
|
884
|
+
};
|
|
910
885
|
|
|
911
886
|
private getWaitExpectedTimeoutLength = () => {
|
|
912
887
|
if (!this._rttObserved.length) {
|
|
913
|
-
return DEFAULT_EXPECTED_EVENT_WAIT_TIME
|
|
888
|
+
return DEFAULT_EXPECTED_EVENT_WAIT_TIME;
|
|
914
889
|
}
|
|
915
890
|
|
|
916
891
|
// 1.5 * RTT
|
|
917
892
|
return (
|
|
918
|
-
(this._rttObserved.reduce((acc, cur) => acc + cur)
|
|
919
|
-
this._rttObserved.length)
|
|
920
|
-
1.5
|
|
921
|
-
)
|
|
922
|
-
}
|
|
893
|
+
(this._rttObserved.reduce((acc, cur) => acc + cur)
|
|
894
|
+
/ this._rttObserved.length)
|
|
895
|
+
* 1.5
|
|
896
|
+
);
|
|
897
|
+
};
|
|
923
898
|
|
|
924
899
|
private heartbeat(immediate?: boolean) {
|
|
925
|
-
this.clearHeartbeat()
|
|
900
|
+
this.clearHeartbeat();
|
|
926
901
|
// @ts-ignore
|
|
927
902
|
this._pingTimeoutId = setTimeout(
|
|
928
903
|
async () => {
|
|
929
904
|
try {
|
|
930
905
|
if (!this._ws || this._ws.readyState !== WS_READY_STATE.OPEN) {
|
|
931
906
|
// no need to ping
|
|
932
|
-
return
|
|
907
|
+
return;
|
|
933
908
|
}
|
|
934
909
|
|
|
935
|
-
this._lastPingSendTS = Date.now()
|
|
936
|
-
await this.ping()
|
|
937
|
-
this._pingFailed = 0
|
|
910
|
+
this._lastPingSendTS = Date.now();
|
|
911
|
+
await this.ping();
|
|
912
|
+
this._pingFailed = 0;
|
|
938
913
|
|
|
939
914
|
// @ts-ignore
|
|
940
915
|
this._pongTimeoutId = setTimeout(() => {
|
|
941
|
-
console.error('pong timed out')
|
|
916
|
+
console.error('pong timed out');
|
|
942
917
|
if (this._pongMissed < DEFAULT_PONG_MISS_TOLERANCE) {
|
|
943
|
-
this._pongMissed
|
|
944
|
-
this.heartbeat(true)
|
|
918
|
+
this._pongMissed++;
|
|
919
|
+
this.heartbeat(true);
|
|
945
920
|
} else {
|
|
946
921
|
// logical perceived connection lost, even though websocket did not receive error or close event
|
|
947
|
-
this.initWebSocketConnection(true)
|
|
922
|
+
this.initWebSocketConnection(true);
|
|
948
923
|
}
|
|
949
|
-
}, this._context.appConfig.realtimePongWaitTimeout)
|
|
924
|
+
}, this._context.appConfig.realtimePongWaitTimeout);
|
|
950
925
|
} catch (e) {
|
|
951
926
|
if (this._pingFailed < DEFAULT_PING_FAIL_TOLERANCE) {
|
|
952
|
-
this._pingFailed
|
|
953
|
-
this.heartbeat()
|
|
927
|
+
this._pingFailed++;
|
|
928
|
+
this.heartbeat();
|
|
954
929
|
} else {
|
|
955
|
-
this.close(CLOSE_EVENT_CODE.HeartbeatPingError)
|
|
930
|
+
this.close(CLOSE_EVENT_CODE.HeartbeatPingError);
|
|
956
931
|
}
|
|
957
932
|
}
|
|
958
933
|
},
|
|
959
934
|
immediate ? 0 : this._context.appConfig.realtimePingInterval
|
|
960
|
-
)
|
|
935
|
+
);
|
|
961
936
|
}
|
|
962
937
|
|
|
963
938
|
private ping = async () => {
|
|
@@ -965,28 +940,28 @@ export class RealtimeWebSocketClient {
|
|
|
965
940
|
watchId: undefined,
|
|
966
941
|
requestId: genRequestId(),
|
|
967
942
|
msgType: 'PING',
|
|
968
|
-
msgData: null
|
|
969
|
-
}
|
|
943
|
+
msgData: null,
|
|
944
|
+
};
|
|
970
945
|
await this.send({
|
|
971
|
-
msg
|
|
972
|
-
})
|
|
946
|
+
msg,
|
|
947
|
+
});
|
|
973
948
|
// console.log('ping sent')
|
|
974
|
-
}
|
|
949
|
+
};
|
|
975
950
|
|
|
976
951
|
private onWatchStart = (client: VirtualWebSocketClient, queryID: string) => {
|
|
977
|
-
this._queryIdClientMap.set(queryID, client)
|
|
978
|
-
}
|
|
952
|
+
this._queryIdClientMap.set(queryID, client);
|
|
953
|
+
};
|
|
979
954
|
|
|
980
955
|
private onWatchClose = (client: VirtualWebSocketClient, queryID: string) => {
|
|
981
956
|
if (queryID) {
|
|
982
|
-
this._queryIdClientMap.delete(queryID)
|
|
957
|
+
this._queryIdClientMap.delete(queryID);
|
|
983
958
|
}
|
|
984
|
-
this._watchIdClientMap.delete(client.watchId)
|
|
985
|
-
this._virtualWSClient.delete(client)
|
|
959
|
+
this._watchIdClientMap.delete(client.watchId);
|
|
960
|
+
this._virtualWSClient.delete(client);
|
|
986
961
|
|
|
987
962
|
if (!this._virtualWSClient.size) {
|
|
988
963
|
// no more existing watch, we should release the websocket connection
|
|
989
|
-
this.close(CLOSE_EVENT_CODE.NoRealtimeListeners)
|
|
964
|
+
this.close(CLOSE_EVENT_CODE.NoRealtimeListeners);
|
|
990
965
|
}
|
|
991
|
-
}
|
|
966
|
+
};
|
|
992
967
|
}
|