@fluidframework/driver-base 1.4.0-121020 → 2.0.0-dev-rc.1.0.0.225277
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 +8 -7
- package/.mocharc.js +12 -0
- package/CHANGELOG.md +117 -0
- package/README.md +37 -1
- package/api-extractor-esm.json +4 -0
- package/api-extractor-lint.json +4 -0
- package/api-extractor.json +2 -2
- package/api-report/driver-base.api.md +112 -0
- package/dist/{documentDeltaConnection.js → documentDeltaConnection.cjs} +228 -113
- package/dist/documentDeltaConnection.cjs.map +1 -0
- package/dist/documentDeltaConnection.d.ts +34 -18
- package/dist/documentDeltaConnection.d.ts.map +1 -1
- package/dist/driver-base-alpha.d.ts +26 -0
- package/dist/driver-base-beta.d.ts +30 -0
- package/dist/driver-base-public.d.ts +30 -0
- package/dist/driver-base-untrimmed.d.ts +213 -0
- package/dist/driverUtils.cjs +146 -0
- package/dist/driverUtils.cjs.map +1 -0
- package/dist/driverUtils.d.ts +36 -0
- package/dist/driverUtils.d.ts.map +1 -0
- package/dist/index.cjs +14 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/{packageVersion.js → packageVersion.cjs} +2 -2
- package/dist/packageVersion.cjs.map +1 -0
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/tsdoc-metadata.json +11 -0
- package/lib/{documentDeltaConnection.d.ts → documentDeltaConnection.d.mts} +35 -19
- package/lib/documentDeltaConnection.d.mts.map +1 -0
- package/lib/{documentDeltaConnection.js → documentDeltaConnection.mjs} +218 -104
- package/lib/documentDeltaConnection.mjs.map +1 -0
- package/lib/driver-base-alpha.d.mts +26 -0
- package/lib/driver-base-beta.d.mts +30 -0
- package/lib/driver-base-public.d.mts +30 -0
- package/lib/driver-base-untrimmed.d.mts +213 -0
- package/lib/driverUtils.d.mts +36 -0
- package/lib/driverUtils.d.mts.map +1 -0
- package/lib/driverUtils.mjs +140 -0
- package/lib/driverUtils.mjs.map +1 -0
- package/lib/index.d.mts +7 -0
- package/lib/index.d.mts.map +1 -0
- package/lib/index.mjs +7 -0
- package/lib/index.mjs.map +1 -0
- package/lib/{packageVersion.d.ts → packageVersion.d.mts} +2 -2
- package/lib/packageVersion.d.mts.map +1 -0
- package/lib/{packageVersion.js → packageVersion.mjs} +2 -2
- package/lib/packageVersion.mjs.map +1 -0
- package/package.json +151 -43
- package/{lib/index.d.ts → prettier.config.cjs} +4 -2
- package/src/documentDeltaConnection.ts +748 -563
- package/src/driverUtils.ts +159 -0
- package/src/index.ts +2 -1
- package/src/packageVersion.ts +1 -1
- package/tsc-multi.test.json +4 -0
- package/tsconfig.json +11 -13
- package/dist/documentDeltaConnection.js.map +0 -1
- package/dist/index.js +0 -18
- package/dist/index.js.map +0 -1
- package/dist/packageVersion.js.map +0 -1
- package/lib/documentDeltaConnection.d.ts.map +0 -1
- package/lib/documentDeltaConnection.js.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js +0 -6
- package/lib/index.js.map +0 -1
- package/lib/packageVersion.d.ts.map +0 -1
- package/lib/packageVersion.js.map +0 -1
- package/lib/test/types/validateDriverBasePrevious.d.ts +0 -2
- package/lib/test/types/validateDriverBasePrevious.d.ts.map +0 -1
- package/lib/test/types/validateDriverBasePrevious.js +0 -4
- package/lib/test/types/validateDriverBasePrevious.js.map +0 -1
- package/tsconfig.esnext.json +0 -7
|
@@ -2,32 +2,54 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
-
import { assert
|
|
6
|
-
import { createGenericNetworkError } from "@fluidframework/driver-utils";
|
|
5
|
+
import { assert } from "@fluidframework/core-utils";
|
|
6
|
+
import { UsageError, createGenericNetworkError } from "@fluidframework/driver-utils";
|
|
7
7
|
import { ScopeType, } from "@fluidframework/protocol-definitions";
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
import { pkgVersion as driverVersion } from "./packageVersion";
|
|
8
|
+
import { LogLevel } from "@fluidframework/core-interfaces";
|
|
9
|
+
import { extractLogSafeErrorProperties, getCircularReplacer, EventEmitterWithErrorHandling, normalizeError, createChildMonitoringContext, } from "@fluidframework/telemetry-utils";
|
|
10
|
+
import { pkgVersion as driverVersion } from "./packageVersion.mjs";
|
|
11
11
|
/**
|
|
12
|
-
* Represents a connection to a stream of delta updates
|
|
12
|
+
* Represents a connection to a stream of delta updates.
|
|
13
|
+
* @internal
|
|
13
14
|
*/
|
|
14
15
|
export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
|
|
16
|
+
get hasDetails() {
|
|
17
|
+
return !!this._details;
|
|
18
|
+
}
|
|
19
|
+
get disposed() {
|
|
20
|
+
assert(this._disposed || this.socket.connected, 0x244 /* "Socket is closed, but connection is not!" */);
|
|
21
|
+
return this._disposed;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* @deprecated Implementors should manage their own logger or monitoring context
|
|
25
|
+
*/
|
|
26
|
+
get logger() {
|
|
27
|
+
return this.mc.logger;
|
|
28
|
+
}
|
|
29
|
+
get details() {
|
|
30
|
+
if (!this._details) {
|
|
31
|
+
throw new Error("Internal error: calling method before _details is initialized!");
|
|
32
|
+
}
|
|
33
|
+
return this._details;
|
|
34
|
+
}
|
|
15
35
|
/**
|
|
16
36
|
* @param socket - websocket to be used
|
|
17
37
|
* @param documentId - ID of the document
|
|
18
38
|
* @param logger - for reporting telemetry events
|
|
19
39
|
* @param enableLongPollingDowngrades - allow connection to be downgraded to long-polling on websocket failure
|
|
20
40
|
*/
|
|
21
|
-
constructor(socket, documentId, logger, enableLongPollingDowngrades = false) {
|
|
41
|
+
constructor(socket, documentId, logger, enableLongPollingDowngrades = false, connectionId) {
|
|
22
42
|
super((name, error) => {
|
|
43
|
+
this.addPropsToError(error);
|
|
23
44
|
logger.sendErrorEvent({
|
|
24
45
|
eventName: "DeltaConnection:EventException",
|
|
25
|
-
name,
|
|
46
|
+
name: name,
|
|
26
47
|
}, error);
|
|
27
48
|
});
|
|
28
49
|
this.socket = socket;
|
|
29
50
|
this.documentId = documentId;
|
|
30
51
|
this.enableLongPollingDowngrades = enableLongPollingDowngrades;
|
|
52
|
+
this.connectionId = connectionId;
|
|
31
53
|
// Listen for ops sent before we receive a response to connect_document
|
|
32
54
|
this.queuedMessages = [];
|
|
33
55
|
this.queuedSignals = [];
|
|
@@ -49,10 +71,15 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
|
|
|
49
71
|
this.queuedMessages.push(...msgs);
|
|
50
72
|
};
|
|
51
73
|
this.earlySignalHandler = (msg) => {
|
|
52
|
-
|
|
74
|
+
if (Array.isArray(msg)) {
|
|
75
|
+
this.queuedSignals.push(...msg);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
this.queuedSignals.push(msg);
|
|
79
|
+
}
|
|
53
80
|
};
|
|
54
|
-
this.mc =
|
|
55
|
-
this.on("newListener", (event,
|
|
81
|
+
this.mc = createChildMonitoringContext({ logger, namespace: "DeltaConnection" });
|
|
82
|
+
this.on("newListener", (event, _listener) => {
|
|
56
83
|
assert(!this.disposed, 0x20a /* "register for event on disposed object" */);
|
|
57
84
|
// Some events are already forwarded - see this.addTrackedListener() calls in initialize().
|
|
58
85
|
if (DocumentDeltaConnection.eventsAlwaysForwarded.includes(event)) {
|
|
@@ -69,31 +96,29 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
|
|
|
69
96
|
// though some logic (naming assert in initialMessages getter) might need to be adjusted (it becomes noop)
|
|
70
97
|
assert((this.listeners(event).length !== 0) === this.trackedListeners.has(event), 0x20b /* "mismatch" */);
|
|
71
98
|
if (!this.trackedListeners.has(event)) {
|
|
72
|
-
|
|
73
|
-
this
|
|
74
|
-
|
|
99
|
+
if (event === "pong") {
|
|
100
|
+
// Empty callback for tracking purposes in this class
|
|
101
|
+
this.trackedListeners.set("pong", () => { });
|
|
102
|
+
const sendPingLoop = () => {
|
|
103
|
+
const start = Date.now();
|
|
104
|
+
this.socket.volatile?.emit("ping", () => {
|
|
105
|
+
this.emit("pong", Date.now() - start);
|
|
106
|
+
// Schedule another ping event in 1 minute
|
|
107
|
+
this.trackLatencyTimeout = setTimeout(() => {
|
|
108
|
+
sendPingLoop();
|
|
109
|
+
}, 1000 * 60);
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
sendPingLoop();
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
this.addTrackedListener(event, (...args) => {
|
|
116
|
+
this.emit(event, ...args);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
75
119
|
}
|
|
76
120
|
});
|
|
77
121
|
}
|
|
78
|
-
get hasDetails() {
|
|
79
|
-
return !!this._details;
|
|
80
|
-
}
|
|
81
|
-
get disposed() {
|
|
82
|
-
assert(this._disposed || this.socket.connected, 0x244 /* "Socket is closed, but connection is not!" */);
|
|
83
|
-
return this._disposed;
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* @deprecated - Implementors should manage their own logger or monitoring context
|
|
87
|
-
*/
|
|
88
|
-
get logger() {
|
|
89
|
-
return this.mc.logger;
|
|
90
|
-
}
|
|
91
|
-
get details() {
|
|
92
|
-
if (!this._details) {
|
|
93
|
-
throw new Error("Internal error: calling method before _details is initialized!");
|
|
94
|
-
}
|
|
95
|
-
return this._details;
|
|
96
|
-
}
|
|
97
122
|
/**
|
|
98
123
|
* Get the ID of the client who is sending the message
|
|
99
124
|
*
|
|
@@ -146,7 +171,7 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
|
|
|
146
171
|
get serviceConfiguration() {
|
|
147
172
|
return this.details.serviceConfiguration;
|
|
148
173
|
}
|
|
149
|
-
|
|
174
|
+
checkNotDisposed() {
|
|
150
175
|
assert(!this.disposed, 0x20c /* "connection disposed" */);
|
|
151
176
|
}
|
|
152
177
|
/**
|
|
@@ -155,7 +180,7 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
|
|
|
155
180
|
* @returns messages sent during the connection
|
|
156
181
|
*/
|
|
157
182
|
get initialMessages() {
|
|
158
|
-
this.
|
|
183
|
+
this.checkNotDisposed();
|
|
159
184
|
// If we call this when the earlyOpHandler is not attached, then the queuedMessages may not include the
|
|
160
185
|
// latest ops. This could possibly indicate that initialMessages was called twice.
|
|
161
186
|
assert(this.earlyOpHandlerAttached, 0x08e /* "Potentially missed initial messages" */);
|
|
@@ -177,7 +202,7 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
|
|
|
177
202
|
* @returns signals sent during the connection
|
|
178
203
|
*/
|
|
179
204
|
get initialSignals() {
|
|
180
|
-
this.
|
|
205
|
+
this.checkNotDisposed();
|
|
181
206
|
assert(this.listeners("signal").length !== 0, 0x090 /* "No signal handler is setup!" */);
|
|
182
207
|
this.removeEarlySignalHandler();
|
|
183
208
|
if (this.queuedSignals.length > 0) {
|
|
@@ -194,7 +219,7 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
|
|
|
194
219
|
* @returns initial client list sent during the connection
|
|
195
220
|
*/
|
|
196
221
|
get initialClients() {
|
|
197
|
-
this.
|
|
222
|
+
this.checkNotDisposed();
|
|
198
223
|
return this.details.initialClients;
|
|
199
224
|
}
|
|
200
225
|
emitMessages(type, messages) {
|
|
@@ -205,59 +230,89 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
|
|
|
205
230
|
this.socket.emit(type, this.clientId, messages);
|
|
206
231
|
}
|
|
207
232
|
}
|
|
208
|
-
submitCore(type, messages) {
|
|
209
|
-
this.emitMessages(type, [messages]);
|
|
210
|
-
}
|
|
211
233
|
/**
|
|
212
234
|
* Submits a new delta operation to the server
|
|
213
235
|
*
|
|
214
236
|
* @param message - delta operation to submit
|
|
215
237
|
*/
|
|
216
238
|
submit(messages) {
|
|
217
|
-
this.
|
|
218
|
-
this.
|
|
239
|
+
this.checkNotDisposed();
|
|
240
|
+
this.emitMessages("submitOp", [messages]);
|
|
219
241
|
}
|
|
220
242
|
/**
|
|
221
243
|
* Submits a new signal to the server
|
|
222
244
|
*
|
|
223
|
-
* @param
|
|
245
|
+
* @param content - Content of the signal.
|
|
246
|
+
* @param targetClientId - When specified, the signal is only sent to the provided client id.
|
|
247
|
+
*/
|
|
248
|
+
submitSignal(content, targetClientId) {
|
|
249
|
+
this.checkNotDisposed();
|
|
250
|
+
if (targetClientId && this.details.supportedFeatures?.submit_signals_v2 !== true) {
|
|
251
|
+
throw new UsageError("Sending signals to specific client ids is not supported.");
|
|
252
|
+
}
|
|
253
|
+
this.emitMessages("submitSignal", [[content]]);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Disconnect from the websocket and close the websocket too.
|
|
224
257
|
*/
|
|
225
|
-
|
|
226
|
-
this.
|
|
227
|
-
|
|
258
|
+
closeSocket(error) {
|
|
259
|
+
if (this._disposed) {
|
|
260
|
+
// This would be rare situation due to complexity around socket emitting events.
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
this.closeSocketCore(error);
|
|
264
|
+
}
|
|
265
|
+
closeSocketCore(error) {
|
|
266
|
+
this.disconnect(error);
|
|
228
267
|
}
|
|
229
268
|
/**
|
|
230
|
-
* Disconnect from the websocket, and permanently disable this DocumentDeltaConnection.
|
|
269
|
+
* Disconnect from the websocket, and permanently disable this DocumentDeltaConnection and close the socket.
|
|
270
|
+
* However the OdspDocumentDeltaConnection differ in dispose as in there we don't close the socket. There is no
|
|
271
|
+
* multiplexing here, so we need to close the socket here.
|
|
231
272
|
*/
|
|
232
273
|
dispose() {
|
|
233
|
-
this.
|
|
234
|
-
|
|
274
|
+
this.logger.sendTelemetryEvent({
|
|
275
|
+
eventName: "ClientClosingDeltaConnection",
|
|
276
|
+
driverVersion,
|
|
277
|
+
details: JSON.stringify({
|
|
278
|
+
...this.getConnectionDetailsProps(),
|
|
279
|
+
}),
|
|
280
|
+
});
|
|
281
|
+
this.disconnect(createGenericNetworkError(
|
|
235
282
|
// pre-0.58 error message: clientClosingConnection
|
|
236
283
|
"Client closing delta connection", { canRetry: true }, { driverVersion }));
|
|
237
284
|
}
|
|
238
|
-
|
|
285
|
+
disconnect(err) {
|
|
239
286
|
// Can't check this.disposed here, as we get here on socket closure,
|
|
240
287
|
// so _disposed & socket.connected might be not in sync while processing
|
|
241
288
|
// "dispose" event.
|
|
242
289
|
if (this._disposed) {
|
|
243
290
|
return;
|
|
244
291
|
}
|
|
292
|
+
if (this.trackLatencyTimeout !== undefined) {
|
|
293
|
+
clearTimeout(this.trackLatencyTimeout);
|
|
294
|
+
this.trackLatencyTimeout = undefined;
|
|
295
|
+
}
|
|
245
296
|
// We set the disposed flag as a part of the contract for overriding the disconnect method. This is used by
|
|
246
297
|
// DocumentDeltaConnection to determine if emitting messages (ops) on the socket is allowed, which is
|
|
247
298
|
// important since OdspDocumentDeltaConnection reuses the socket rather than truly disconnecting it. Note that
|
|
248
299
|
// OdspDocumentDeltaConnection may still send disconnect_document which is allowed; this is only intended
|
|
249
300
|
// to prevent normal messages from being emitted.
|
|
250
301
|
this._disposed = true;
|
|
302
|
+
// Remove all listeners listening on the socket. These are listeners on socket and not on this connection
|
|
303
|
+
// object. Anyway since we have disposed this connection object, nobody should listen to event on socket
|
|
304
|
+
// anymore.
|
|
251
305
|
this.removeTrackedListeners();
|
|
252
|
-
|
|
306
|
+
// Clear the connection/socket before letting the deltaManager/connection manager know about the disconnect.
|
|
307
|
+
this.disconnectCore();
|
|
308
|
+
// Let user of connection object know about disconnect.
|
|
309
|
+
this.emit("disconnect", err);
|
|
253
310
|
}
|
|
254
311
|
/**
|
|
255
312
|
* Disconnect from the websocket.
|
|
256
|
-
* @param socketProtocolError - true if error happened on socket / socket.io protocol level
|
|
257
|
-
* (not on Fluid protocol level)
|
|
258
313
|
* @param reason - reason for disconnect
|
|
259
314
|
*/
|
|
260
|
-
|
|
315
|
+
disconnectCore() {
|
|
261
316
|
this.socket.disconnect();
|
|
262
317
|
}
|
|
263
318
|
async initialize(connectMessage, timeout) {
|
|
@@ -267,23 +322,56 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
|
|
|
267
322
|
// Socket.io's reconnect_attempt event is unreliable, so we track connect_error count instead.
|
|
268
323
|
let internalSocketConnectionFailureCount = 0;
|
|
269
324
|
const isInternalSocketReconnectionEnabled = () => this.socket.io.reconnection();
|
|
270
|
-
const getMaxInternalSocketReconnectionAttempts = () => isInternalSocketReconnectionEnabled()
|
|
271
|
-
? this.socket.io.reconnectionAttempts()
|
|
272
|
-
: 0;
|
|
325
|
+
const getMaxInternalSocketReconnectionAttempts = () => isInternalSocketReconnectionEnabled() ? this.socket.io.reconnectionAttempts() : 0;
|
|
273
326
|
const getMaxAllowedInternalSocketConnectionFailures = () => getMaxInternalSocketReconnectionAttempts() + 1;
|
|
274
327
|
this._details = await new Promise((resolve, reject) => {
|
|
275
|
-
const
|
|
276
|
-
|
|
328
|
+
const failAndCloseSocket = (err) => {
|
|
329
|
+
try {
|
|
330
|
+
this.closeSocket(err);
|
|
331
|
+
}
|
|
332
|
+
catch (failError) {
|
|
333
|
+
const normalizedError = this.addPropsToError(failError);
|
|
334
|
+
this.logger.sendErrorEvent({ eventName: "CloseSocketError" }, normalizedError);
|
|
335
|
+
}
|
|
336
|
+
reject(err);
|
|
337
|
+
};
|
|
338
|
+
const failConnection = (err) => {
|
|
339
|
+
try {
|
|
340
|
+
this.disconnect(err);
|
|
341
|
+
}
|
|
342
|
+
catch (failError) {
|
|
343
|
+
const normalizedError = this.addPropsToError(failError);
|
|
344
|
+
this.logger.sendErrorEvent({ eventName: "FailConnectionError" }, normalizedError);
|
|
345
|
+
}
|
|
277
346
|
reject(err);
|
|
278
347
|
};
|
|
348
|
+
// Immediately set the connection timeout.
|
|
349
|
+
// Give extra 2 seconds for handshake on top of socket connection timeout.
|
|
350
|
+
this.socketConnectionTimeout = setTimeout(() => {
|
|
351
|
+
failConnection(this.createErrorObject("orderingServiceHandshakeTimeout"));
|
|
352
|
+
}, timeout + 2000);
|
|
279
353
|
// Listen for connection issues
|
|
280
354
|
this.addConnectionListener("connect_error", (error) => {
|
|
281
|
-
var _a;
|
|
282
355
|
internalSocketConnectionFailureCount++;
|
|
283
356
|
let isWebSocketTransportError = false;
|
|
284
357
|
try {
|
|
285
|
-
const description = error
|
|
286
|
-
|
|
358
|
+
const description = error?.description;
|
|
359
|
+
const context = error?.context;
|
|
360
|
+
if (context && typeof context === "object") {
|
|
361
|
+
const statusText = context.statusText?.code;
|
|
362
|
+
// Self-Signed Certificate ErrorCode Found in error.context
|
|
363
|
+
if (statusText === "DEPTH_ZERO_SELF_SIGNED_CERT") {
|
|
364
|
+
failAndCloseSocket(this.createErrorObject("connect_error", error, false));
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
else if (description && typeof description === "object") {
|
|
369
|
+
const errorCode = description.error?.code;
|
|
370
|
+
// Self-Signed Certificate ErrorCode Found in error.description
|
|
371
|
+
if (errorCode === "DEPTH_ZERO_SELF_SIGNED_CERT") {
|
|
372
|
+
failAndCloseSocket(this.createErrorObject("connect_error", error, false));
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
287
375
|
if (error.type === "TransportError") {
|
|
288
376
|
isWebSocketTransportError = true;
|
|
289
377
|
}
|
|
@@ -295,7 +383,7 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
|
|
|
295
383
|
// Handle socket transport downgrading when not offline.
|
|
296
384
|
if (isWebSocketTransportError &&
|
|
297
385
|
this.enableLongPollingDowngrades &&
|
|
298
|
-
|
|
386
|
+
this.socket.io.opts.transports?.[0] !== "polling") {
|
|
299
387
|
// Downgrade transports to polling upgrade mechanism.
|
|
300
388
|
this.socket.io.opts.transports = ["polling", "websocket"];
|
|
301
389
|
// Don't alter reconnection behavior if already enabled.
|
|
@@ -307,15 +395,16 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
|
|
|
307
395
|
}
|
|
308
396
|
// Allow built-in socket.io reconnection handling.
|
|
309
397
|
if (isInternalSocketReconnectionEnabled() &&
|
|
310
|
-
internalSocketConnectionFailureCount <
|
|
398
|
+
internalSocketConnectionFailureCount <
|
|
399
|
+
getMaxAllowedInternalSocketConnectionFailures()) {
|
|
311
400
|
// Reconnection is enabled and maximum reconnect attempts have not been reached.
|
|
312
401
|
return;
|
|
313
402
|
}
|
|
314
|
-
|
|
403
|
+
failAndCloseSocket(this.createErrorObject("connect_error", error));
|
|
315
404
|
});
|
|
316
405
|
// Listen for timeouts
|
|
317
406
|
this.addConnectionListener("connect_timeout", () => {
|
|
318
|
-
|
|
407
|
+
failAndCloseSocket(this.createErrorObject("connect_timeout"));
|
|
319
408
|
});
|
|
320
409
|
this.addConnectionListener("connect_document_success", (response) => {
|
|
321
410
|
// If we sent a nonce and the server supports nonces, check that the nonces match
|
|
@@ -331,16 +420,20 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
|
|
|
331
420
|
// The only time we expect a mismatch in requested/actual is if we lack write permissions
|
|
332
421
|
// In this case we will get "read", even if we requested "write"
|
|
333
422
|
if (actualMode !== requestedMode) {
|
|
334
|
-
|
|
423
|
+
failConnection(this.createErrorObject("connect_document_success", "Connected in a different mode than was requested", false));
|
|
335
424
|
return;
|
|
336
425
|
}
|
|
337
426
|
}
|
|
338
427
|
else {
|
|
339
428
|
if (actualMode === "write") {
|
|
340
|
-
|
|
429
|
+
failConnection(this.createErrorObject("connect_document_success", "Connected in write mode without write permissions", false));
|
|
341
430
|
return;
|
|
342
431
|
}
|
|
343
432
|
}
|
|
433
|
+
this.logger.sendTelemetryEvent({
|
|
434
|
+
eventName: "ConnectDocumentSuccess",
|
|
435
|
+
pendingClientId: response.clientId,
|
|
436
|
+
}, undefined, LogLevel.verbose);
|
|
344
437
|
this.checkpointSequenceNumber = response.checkpointSequenceNumber;
|
|
345
438
|
this.removeConnectionListeners();
|
|
346
439
|
resolve(response);
|
|
@@ -348,20 +441,20 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
|
|
|
348
441
|
// Socket can be disconnected while waiting for Fluid protocol messages
|
|
349
442
|
// (connect_document_error / connect_document_success), as well as before DeltaManager
|
|
350
443
|
// had a chance to register its handlers.
|
|
351
|
-
this.addTrackedListener("disconnect", (reason) => {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
444
|
+
this.addTrackedListener("disconnect", (reason, details) => {
|
|
445
|
+
failAndCloseSocket(this.createErrorObjectWithProps("disconnect", reason, {
|
|
446
|
+
socketErrorType: details?.context?.type,
|
|
447
|
+
// https://www.rfc-editor.org/rfc/rfc6455#section-7.4
|
|
448
|
+
socketCode: details?.context?.code,
|
|
449
|
+
}));
|
|
355
450
|
});
|
|
356
|
-
this.addTrackedListener("error", (
|
|
357
|
-
// First, raise an error event, to give clients a chance to observe error contents
|
|
451
|
+
this.addTrackedListener("error", (error) => {
|
|
358
452
|
// This includes "Invalid namespace" error, which we consider critical (reconnecting will not help)
|
|
359
453
|
const err = this.createErrorObject("error", error, error !== "Invalid namespace");
|
|
360
|
-
this.emit("error", err);
|
|
361
454
|
// Disconnect socket - required if happened before initial handshake
|
|
362
|
-
|
|
363
|
-
})
|
|
364
|
-
this.addConnectionListener("connect_document_error", (
|
|
455
|
+
failAndCloseSocket(err);
|
|
456
|
+
});
|
|
457
|
+
this.addConnectionListener("connect_document_error", (error) => {
|
|
365
458
|
// If we sent a nonce and the server supports nonces, check that the nonces match
|
|
366
459
|
if (connectMessage.nonce !== undefined &&
|
|
367
460
|
error.nonce !== undefined &&
|
|
@@ -370,16 +463,30 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
|
|
|
370
463
|
}
|
|
371
464
|
// This is not an socket.io error - it's Fluid protocol error.
|
|
372
465
|
// In this case fail connection and indicate that we were unable to create connection
|
|
373
|
-
|
|
374
|
-
})
|
|
466
|
+
failConnection(this.createErrorObject("connect_document_error", error));
|
|
467
|
+
});
|
|
375
468
|
this.socket.emit("connect_document", connectMessage);
|
|
376
|
-
// Give extra 2 seconds for handshake on top of socket connection timeout
|
|
377
|
-
this.socketConnectionTimeout = setTimeout(() => {
|
|
378
|
-
fail(false, this.createErrorObject("orderingServiceHandshakeTimeout"));
|
|
379
|
-
}, timeout + 2000);
|
|
380
469
|
});
|
|
381
470
|
assert(!this.disposed, 0x246 /* "checking consistency of socket & _disposed flags" */);
|
|
382
471
|
}
|
|
472
|
+
addPropsToError(errorToBeNormalized) {
|
|
473
|
+
const normalizedError = normalizeError(errorToBeNormalized, {
|
|
474
|
+
props: {
|
|
475
|
+
details: JSON.stringify({
|
|
476
|
+
...this.getConnectionDetailsProps(),
|
|
477
|
+
}),
|
|
478
|
+
},
|
|
479
|
+
});
|
|
480
|
+
return normalizedError;
|
|
481
|
+
}
|
|
482
|
+
getConnectionDetailsProps() {
|
|
483
|
+
return {
|
|
484
|
+
disposed: this._disposed,
|
|
485
|
+
socketConnected: this.socket?.connected,
|
|
486
|
+
clientId: this._details?.clientId,
|
|
487
|
+
connectionId: this.connectionId,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
383
490
|
removeEarlyOpHandler() {
|
|
384
491
|
this.socket.removeListener("op", this.earlyOpHandler);
|
|
385
492
|
this.earlyOpHandlerAttached = false;
|
|
@@ -418,33 +525,40 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
|
|
|
418
525
|
}
|
|
419
526
|
this.connectionListeners.clear();
|
|
420
527
|
}
|
|
528
|
+
getErrorMessage(error) {
|
|
529
|
+
if (error?.type !== "TransportError") {
|
|
530
|
+
return extractLogSafeErrorProperties(error, true).message;
|
|
531
|
+
}
|
|
532
|
+
// JSON.stringify drops Error.message
|
|
533
|
+
const messagePrefix = error?.message !== undefined ? `${error.message}: ` : "";
|
|
534
|
+
// Websocket errors reported by engine.io-client.
|
|
535
|
+
// They are Error objects with description containing WS error and description = "TransportError"
|
|
536
|
+
// Please see https://github.com/socketio/engine.io-client/blob/7245b80/lib/transport.ts#L44,
|
|
537
|
+
return `${messagePrefix}${JSON.stringify(error, getCircularReplacer())}`;
|
|
538
|
+
}
|
|
539
|
+
createErrorObjectWithProps(handler, error, props, canRetry = true) {
|
|
540
|
+
return createGenericNetworkError(`socket.io (${handler}): ${this.getErrorMessage(error)}`, { canRetry }, {
|
|
541
|
+
...props,
|
|
542
|
+
driverVersion,
|
|
543
|
+
details: JSON.stringify({
|
|
544
|
+
...this.getConnectionDetailsProps(),
|
|
545
|
+
}),
|
|
546
|
+
});
|
|
547
|
+
}
|
|
421
548
|
/**
|
|
422
549
|
* Error raising for socket.io issues
|
|
423
550
|
*/
|
|
424
551
|
createErrorObject(handler, error, canRetry = true) {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
const messagePrefix = ((error === null || error === void 0 ? void 0 : error.message) !== undefined)
|
|
432
|
-
? `${error.message}: `
|
|
433
|
-
: "";
|
|
434
|
-
// Websocket errors reported by engine.io-client.
|
|
435
|
-
// They are Error objects with description containing WS error and description = "TransportError"
|
|
436
|
-
// Please see https://github.com/socketio/engine.io-client/blob/7245b80/lib/transport.ts#L44,
|
|
437
|
-
message = `${messagePrefix}${JSON.stringify(error, getCircularReplacer())}`;
|
|
438
|
-
}
|
|
439
|
-
else {
|
|
440
|
-
message = extractLogSafeErrorProperties(error).message;
|
|
441
|
-
}
|
|
442
|
-
const errorObj = createGenericNetworkError(`socket.io (${handler}): ${message}`, { canRetry }, { driverVersion });
|
|
443
|
-
return errorObj;
|
|
552
|
+
return createGenericNetworkError(`socket.io (${handler}): ${this.getErrorMessage(error)}`, { canRetry }, {
|
|
553
|
+
driverVersion,
|
|
554
|
+
details: JSON.stringify({
|
|
555
|
+
...this.getConnectionDetailsProps(),
|
|
556
|
+
}),
|
|
557
|
+
});
|
|
444
558
|
}
|
|
445
559
|
}
|
|
446
560
|
DocumentDeltaConnection.eventsToForward = ["nack", "op", "signal", "pong"];
|
|
447
561
|
// WARNING: These are critical events that we can't miss, so registration for them has to be in place at all times!
|
|
448
562
|
// Including before handshake is over, and after that (but before DeltaManager had a chance to put its own handlers)
|
|
449
563
|
DocumentDeltaConnection.eventsAlwaysForwarded = ["disconnect", "error"];
|
|
450
|
-
//# sourceMappingURL=documentDeltaConnection.
|
|
564
|
+
//# sourceMappingURL=documentDeltaConnection.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"documentDeltaConnection.mjs","sourceRoot":"","sources":["../src/documentDeltaConnection.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/core-utils\";\nimport {\n\tIAnyDriverError,\n\tIDocumentDeltaConnection,\n\tIDocumentDeltaConnectionEvents,\n} from \"@fluidframework/driver-definitions\";\nimport { UsageError, createGenericNetworkError } from \"@fluidframework/driver-utils\";\nimport {\n\tConnectionMode,\n\tIClientConfiguration,\n\tIConnect,\n\tIConnected,\n\tIDocumentMessage,\n\tISequencedDocumentMessage,\n\tISignalClient,\n\tISignalMessage,\n\tITokenClaims,\n\tScopeType,\n} from \"@fluidframework/protocol-definitions\";\nimport { IDisposable, ITelemetryProperties, LogLevel } from \"@fluidframework/core-interfaces\";\nimport {\n\tITelemetryLoggerExt,\n\textractLogSafeErrorProperties,\n\tgetCircularReplacer,\n\tMonitoringContext,\n\tEventEmitterWithErrorHandling,\n\tnormalizeError,\n\tcreateChildMonitoringContext,\n} from \"@fluidframework/telemetry-utils\";\nimport type { Socket } from \"socket.io-client\";\n// For now, this package is versioned and released in unison with the specific drivers\nimport { pkgVersion as driverVersion } from \"./packageVersion\";\n\n/**\n * Represents a connection to a stream of delta updates.\n * @internal\n */\nexport class DocumentDeltaConnection\n\textends EventEmitterWithErrorHandling<IDocumentDeltaConnectionEvents>\n\timplements IDocumentDeltaConnection, IDisposable\n{\n\tstatic readonly eventsToForward = [\"nack\", \"op\", \"signal\", \"pong\"];\n\n\t// WARNING: These are critical events that we can't miss, so registration for them has to be in place at all times!\n\t// Including before handshake is over, and after that (but before DeltaManager had a chance to put its own handlers)\n\tstatic readonly eventsAlwaysForwarded = [\"disconnect\", \"error\"];\n\n\t/**\n\t * Last known sequence number to ordering service at the time of connection\n\t * It may lap actual last sequence number (quite a bit, if container is very active).\n\t * But it's best information for client to figure out how far it is behind, at least\n\t * for \"read\" connections. \"write\" connections may use own \"join\" op to similar information,\n\t * that is likely to be more up-to-date.\n\t */\n\tpublic checkpointSequenceNumber: number | undefined;\n\n\t// Listen for ops sent before we receive a response to connect_document\n\tprotected readonly queuedMessages: ISequencedDocumentMessage[] = [];\n\tprotected readonly queuedSignals: ISignalMessage[] = [];\n\n\t/**\n\t * A flag to indicate whether we have our handler attached. If it's attached, we're queueing incoming ops\n\t * to later be retrieved via initialMessages.\n\t */\n\tprivate earlyOpHandlerAttached: boolean = false;\n\n\tprivate socketConnectionTimeout: ReturnType<typeof setTimeout> | undefined;\n\n\tprivate _details: IConnected | undefined;\n\n\tprivate trackLatencyTimeout: ReturnType<typeof setTimeout> | undefined;\n\n\t// Listeners only needed while the connection is in progress\n\tprivate readonly connectionListeners: Map<string, (...args: any[]) => void> = new Map();\n\t// Listeners used throughout the lifetime of the DocumentDeltaConnection\n\tprivate readonly trackedListeners: Map<string, (...args: any[]) => void> = new Map();\n\n\tprotected get hasDetails(): boolean {\n\t\treturn !!this._details;\n\t}\n\n\tpublic get disposed() {\n\t\tassert(\n\t\t\tthis._disposed || this.socket.connected,\n\t\t\t0x244 /* \"Socket is closed, but connection is not!\" */,\n\t\t);\n\t\treturn this._disposed;\n\t}\n\n\t/**\n\t * Flag to indicate whether the DocumentDeltaConnection is expected to still be capable of sending messages.\n\t * After disconnection, we flip this to prevent any stale messages from being emitted.\n\t */\n\tprotected _disposed: boolean = false;\n\tprivate readonly mc: MonitoringContext;\n\n\t/**\n\t * @deprecated Implementors should manage their own logger or monitoring context\n\t */\n\tprotected get logger(): ITelemetryLoggerExt {\n\t\treturn this.mc.logger;\n\t}\n\n\tpublic get details(): IConnected {\n\t\tif (!this._details) {\n\t\t\tthrow new Error(\"Internal error: calling method before _details is initialized!\");\n\t\t}\n\t\treturn this._details;\n\t}\n\n\t/**\n\t * @param socket - websocket to be used\n\t * @param documentId - ID of the document\n\t * @param logger - for reporting telemetry events\n\t * @param enableLongPollingDowngrades - allow connection to be downgraded to long-polling on websocket failure\n\t */\n\tprotected constructor(\n\t\tprotected readonly socket: Socket,\n\t\tpublic documentId: string,\n\t\tlogger: ITelemetryLoggerExt,\n\t\tprivate readonly enableLongPollingDowngrades: boolean = false,\n\t\tprotected readonly connectionId?: string,\n\t) {\n\t\tsuper((name, error) => {\n\t\t\tthis.addPropsToError(error);\n\t\t\tlogger.sendErrorEvent(\n\t\t\t\t{\n\t\t\t\t\teventName: \"DeltaConnection:EventException\",\n\t\t\t\t\tname: name as string,\n\t\t\t\t},\n\t\t\t\terror,\n\t\t\t);\n\t\t});\n\n\t\tthis.mc = createChildMonitoringContext({ logger, namespace: \"DeltaConnection\" });\n\n\t\tthis.on(\"newListener\", (event, _listener) => {\n\t\t\tassert(!this.disposed, 0x20a /* \"register for event on disposed object\" */);\n\n\t\t\t// Some events are already forwarded - see this.addTrackedListener() calls in initialize().\n\t\t\tif (DocumentDeltaConnection.eventsAlwaysForwarded.includes(event)) {\n\t\t\t\tassert(this.trackedListeners.has(event), 0x245 /* \"tracked listener\" */);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!DocumentDeltaConnection.eventsToForward.includes(event)) {\n\t\t\t\tthrow new Error(`DocumentDeltaConnection: Registering for unknown event: ${event}`);\n\t\t\t}\n\n\t\t\t// Whenever listener is added, we should subscribe on same event on socket, so these two things\n\t\t\t// should be in sync. This currently assumes that nobody unregisters and registers back listeners,\n\t\t\t// and that there are no \"internal\" listeners installed (like \"error\" case we skip above)\n\t\t\t// Better flow might be to always unconditionally register all handlers on successful connection,\n\t\t\t// though some logic (naming assert in initialMessages getter) might need to be adjusted (it becomes noop)\n\t\t\tassert(\n\t\t\t\t(this.listeners(event).length !== 0) === this.trackedListeners.has(event),\n\t\t\t\t0x20b /* \"mismatch\" */,\n\t\t\t);\n\t\t\tif (!this.trackedListeners.has(event)) {\n\t\t\t\tif (event === \"pong\") {\n\t\t\t\t\t// Empty callback for tracking purposes in this class\n\t\t\t\t\tthis.trackedListeners.set(\"pong\", () => {});\n\n\t\t\t\t\tconst sendPingLoop = () => {\n\t\t\t\t\t\tconst start = Date.now();\n\n\t\t\t\t\t\tthis.socket.volatile?.emit(\"ping\", () => {\n\t\t\t\t\t\t\tthis.emit(\"pong\", Date.now() - start);\n\n\t\t\t\t\t\t\t// Schedule another ping event in 1 minute\n\t\t\t\t\t\t\tthis.trackLatencyTimeout = setTimeout(() => {\n\t\t\t\t\t\t\t\tsendPingLoop();\n\t\t\t\t\t\t\t}, 1000 * 60);\n\t\t\t\t\t\t});\n\t\t\t\t\t};\n\n\t\t\t\t\tsendPingLoop();\n\t\t\t\t} else {\n\t\t\t\t\tthis.addTrackedListener(event, (...args: any[]) => {\n\t\t\t\t\t\tthis.emit(event, ...args);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Get the ID of the client who is sending the message\n\t *\n\t * @returns the client ID\n\t */\n\tpublic get clientId(): string {\n\t\treturn this.details.clientId;\n\t}\n\n\t/**\n\t * Get the mode of the client\n\t *\n\t * @returns the client mode\n\t */\n\tpublic get mode(): ConnectionMode {\n\t\treturn this.details.mode;\n\t}\n\n\t/**\n\t * Get the claims of the client who is sending the message\n\t *\n\t * @returns client claims\n\t */\n\tpublic get claims(): ITokenClaims {\n\t\treturn this.details.claims;\n\t}\n\n\t/**\n\t * Get whether or not this is an existing document\n\t *\n\t * @returns true if the document exists\n\t */\n\tpublic get existing(): boolean {\n\t\treturn this.details.existing;\n\t}\n\n\t/**\n\t * Get the maximum size of a message before chunking is required\n\t *\n\t * @returns the maximum size of a message before chunking is required\n\t */\n\tpublic get maxMessageSize(): number {\n\t\treturn this.details.serviceConfiguration.maxMessageSize;\n\t}\n\n\t/**\n\t * Semver of protocol being used with the service\n\t */\n\tpublic get version(): string {\n\t\treturn this.details.version;\n\t}\n\n\t/**\n\t * Configuration details provided by the service\n\t */\n\tpublic get serviceConfiguration(): IClientConfiguration {\n\t\treturn this.details.serviceConfiguration;\n\t}\n\n\tprivate checkNotDisposed() {\n\t\tassert(!this.disposed, 0x20c /* \"connection disposed\" */);\n\t}\n\n\t/**\n\t * Get messages sent during the connection\n\t *\n\t * @returns messages sent during the connection\n\t */\n\tpublic get initialMessages(): ISequencedDocumentMessage[] {\n\t\tthis.checkNotDisposed();\n\n\t\t// If we call this when the earlyOpHandler is not attached, then the queuedMessages may not include the\n\t\t// latest ops. This could possibly indicate that initialMessages was called twice.\n\t\tassert(this.earlyOpHandlerAttached, 0x08e /* \"Potentially missed initial messages\" */);\n\t\t// We will lose ops and perf will tank as we need to go to storage to become current!\n\t\tassert(this.listeners(\"op\").length !== 0, 0x08f /* \"No op handler is setup!\" */);\n\n\t\tthis.removeEarlyOpHandler();\n\n\t\tif (this.queuedMessages.length > 0) {\n\t\t\t// Some messages were queued.\n\t\t\t// add them to the list of initialMessages to be processed\n\t\t\tthis.details.initialMessages.push(...this.queuedMessages);\n\t\t\tthis.details.initialMessages.sort((a, b) => a.sequenceNumber - b.sequenceNumber);\n\t\t\tthis.queuedMessages.length = 0;\n\t\t}\n\t\treturn this.details.initialMessages;\n\t}\n\n\t/**\n\t * Get signals sent during the connection\n\t *\n\t * @returns signals sent during the connection\n\t */\n\tpublic get initialSignals(): ISignalMessage[] {\n\t\tthis.checkNotDisposed();\n\t\tassert(this.listeners(\"signal\").length !== 0, 0x090 /* \"No signal handler is setup!\" */);\n\n\t\tthis.removeEarlySignalHandler();\n\n\t\tif (this.queuedSignals.length > 0) {\n\t\t\t// Some signals were queued.\n\t\t\t// add them to the list of initialSignals to be processed\n\t\t\tthis.details.initialSignals.push(...this.queuedSignals);\n\t\t\tthis.queuedSignals.length = 0;\n\t\t}\n\t\treturn this.details.initialSignals;\n\t}\n\n\t/**\n\t * Get initial client list\n\t *\n\t * @returns initial client list sent during the connection\n\t */\n\tpublic get initialClients(): ISignalClient[] {\n\t\tthis.checkNotDisposed();\n\t\treturn this.details.initialClients;\n\t}\n\n\tprotected emitMessages(type: string, messages: IDocumentMessage[][]) {\n\t\t// Although the implementation here disconnects the socket and does not reuse it, other subclasses\n\t\t// (e.g. OdspDocumentDeltaConnection) may reuse the socket. In these cases, we need to avoid emitting\n\t\t// on the still-live socket.\n\t\tif (!this.disposed) {\n\t\t\tthis.socket.emit(type, this.clientId, messages);\n\t\t}\n\t}\n\n\t/**\n\t * Submits a new delta operation to the server\n\t *\n\t * @param message - delta operation to submit\n\t */\n\tpublic submit(messages: IDocumentMessage[]): void {\n\t\tthis.checkNotDisposed();\n\t\tthis.emitMessages(\"submitOp\", [messages]);\n\t}\n\n\t/**\n\t * Submits a new signal to the server\n\t *\n\t * @param content - Content of the signal.\n\t * @param targetClientId - When specified, the signal is only sent to the provided client id.\n\t */\n\tpublic submitSignal(content: IDocumentMessage, targetClientId?: string): void {\n\t\tthis.checkNotDisposed();\n\n\t\tif (targetClientId && this.details.supportedFeatures?.submit_signals_v2 !== true) {\n\t\t\tthrow new UsageError(\"Sending signals to specific client ids is not supported.\");\n\t\t}\n\n\t\tthis.emitMessages(\"submitSignal\", [[content]]);\n\t}\n\n\t/**\n\t * Disconnect from the websocket and close the websocket too.\n\t */\n\tprivate closeSocket(error: IAnyDriverError) {\n\t\tif (this._disposed) {\n\t\t\t// This would be rare situation due to complexity around socket emitting events.\n\t\t\treturn;\n\t\t}\n\t\tthis.closeSocketCore(error);\n\t}\n\n\tprotected closeSocketCore(error: IAnyDriverError) {\n\t\tthis.disconnect(error);\n\t}\n\n\t/**\n\t * Disconnect from the websocket, and permanently disable this DocumentDeltaConnection and close the socket.\n\t * However the OdspDocumentDeltaConnection differ in dispose as in there we don't close the socket. There is no\n\t * multiplexing here, so we need to close the socket here.\n\t */\n\tpublic dispose() {\n\t\tthis.logger.sendTelemetryEvent({\n\t\t\teventName: \"ClientClosingDeltaConnection\",\n\t\t\tdriverVersion,\n\t\t\tdetails: JSON.stringify({\n\t\t\t\t...this.getConnectionDetailsProps(),\n\t\t\t}),\n\t\t});\n\t\tthis.disconnect(\n\t\t\tcreateGenericNetworkError(\n\t\t\t\t// pre-0.58 error message: clientClosingConnection\n\t\t\t\t\"Client closing delta connection\",\n\t\t\t\t{ canRetry: true },\n\t\t\t\t{ driverVersion },\n\t\t\t),\n\t\t);\n\t}\n\n\tprotected disconnect(err: IAnyDriverError) {\n\t\t// Can't check this.disposed here, as we get here on socket closure,\n\t\t// so _disposed & socket.connected might be not in sync while processing\n\t\t// \"dispose\" event.\n\t\tif (this._disposed) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.trackLatencyTimeout !== undefined) {\n\t\t\tclearTimeout(this.trackLatencyTimeout);\n\t\t\tthis.trackLatencyTimeout = undefined;\n\t\t}\n\n\t\t// We set the disposed flag as a part of the contract for overriding the disconnect method. This is used by\n\t\t// DocumentDeltaConnection to determine if emitting messages (ops) on the socket is allowed, which is\n\t\t// important since OdspDocumentDeltaConnection reuses the socket rather than truly disconnecting it. Note that\n\t\t// OdspDocumentDeltaConnection may still send disconnect_document which is allowed; this is only intended\n\t\t// to prevent normal messages from being emitted.\n\t\tthis._disposed = true;\n\n\t\t// Remove all listeners listening on the socket. These are listeners on socket and not on this connection\n\t\t// object. Anyway since we have disposed this connection object, nobody should listen to event on socket\n\t\t// anymore.\n\t\tthis.removeTrackedListeners();\n\n\t\t// Clear the connection/socket before letting the deltaManager/connection manager know about the disconnect.\n\t\tthis.disconnectCore();\n\n\t\t// Let user of connection object know about disconnect.\n\t\tthis.emit(\"disconnect\", err);\n\t}\n\n\t/**\n\t * Disconnect from the websocket.\n\t * @param reason - reason for disconnect\n\t */\n\tprotected disconnectCore() {\n\t\tthis.socket.disconnect();\n\t}\n\n\tprotected async initialize(connectMessage: IConnect, timeout: number) {\n\t\tthis.socket.on(\"op\", this.earlyOpHandler);\n\t\tthis.socket.on(\"signal\", this.earlySignalHandler);\n\t\tthis.earlyOpHandlerAttached = true;\n\n\t\t// Socket.io's reconnect_attempt event is unreliable, so we track connect_error count instead.\n\t\tlet internalSocketConnectionFailureCount: number = 0;\n\t\tconst isInternalSocketReconnectionEnabled = (): boolean => this.socket.io.reconnection();\n\t\tconst getMaxInternalSocketReconnectionAttempts = (): number =>\n\t\t\tisInternalSocketReconnectionEnabled() ? this.socket.io.reconnectionAttempts() : 0;\n\t\tconst getMaxAllowedInternalSocketConnectionFailures = (): number =>\n\t\t\tgetMaxInternalSocketReconnectionAttempts() + 1;\n\n\t\tthis._details = await new Promise<IConnected>((resolve, reject) => {\n\t\t\tconst failAndCloseSocket = (err: IAnyDriverError) => {\n\t\t\t\ttry {\n\t\t\t\t\tthis.closeSocket(err);\n\t\t\t\t} catch (failError) {\n\t\t\t\t\tconst normalizedError = this.addPropsToError(failError);\n\t\t\t\t\tthis.logger.sendErrorEvent({ eventName: \"CloseSocketError\" }, normalizedError);\n\t\t\t\t}\n\t\t\t\treject(err);\n\t\t\t};\n\n\t\t\tconst failConnection = (err: IAnyDriverError) => {\n\t\t\t\ttry {\n\t\t\t\t\tthis.disconnect(err);\n\t\t\t\t} catch (failError) {\n\t\t\t\t\tconst normalizedError = this.addPropsToError(failError);\n\t\t\t\t\tthis.logger.sendErrorEvent(\n\t\t\t\t\t\t{ eventName: \"FailConnectionError\" },\n\t\t\t\t\t\tnormalizedError,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treject(err);\n\t\t\t};\n\n\t\t\t// Immediately set the connection timeout.\n\t\t\t// Give extra 2 seconds for handshake on top of socket connection timeout.\n\t\t\tthis.socketConnectionTimeout = setTimeout(() => {\n\t\t\t\tfailConnection(this.createErrorObject(\"orderingServiceHandshakeTimeout\"));\n\t\t\t}, timeout + 2000);\n\n\t\t\t// Listen for connection issues\n\t\t\tthis.addConnectionListener(\"connect_error\", (error) => {\n\t\t\t\tinternalSocketConnectionFailureCount++;\n\t\t\t\tlet isWebSocketTransportError = false;\n\t\t\t\ttry {\n\t\t\t\t\tconst description = error?.description;\n\t\t\t\t\tconst context = error?.context;\n\n\t\t\t\t\tif (context && typeof context === \"object\") {\n\t\t\t\t\t\tconst statusText = context.statusText?.code;\n\n\t\t\t\t\t\t// Self-Signed Certificate ErrorCode Found in error.context\n\t\t\t\t\t\tif (statusText === \"DEPTH_ZERO_SELF_SIGNED_CERT\") {\n\t\t\t\t\t\t\tfailAndCloseSocket(\n\t\t\t\t\t\t\t\tthis.createErrorObject(\"connect_error\", error, false),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (description && typeof description === \"object\") {\n\t\t\t\t\t\tconst errorCode = description.error?.code;\n\n\t\t\t\t\t\t// Self-Signed Certificate ErrorCode Found in error.description\n\t\t\t\t\t\tif (errorCode === \"DEPTH_ZERO_SELF_SIGNED_CERT\") {\n\t\t\t\t\t\t\tfailAndCloseSocket(\n\t\t\t\t\t\t\t\tthis.createErrorObject(\"connect_error\", error, false),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (error.type === \"TransportError\") {\n\t\t\t\t\t\t\tisWebSocketTransportError = true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// That's a WebSocket. Clear it as we can't log it.\n\t\t\t\t\t\tdescription.target = undefined;\n\t\t\t\t\t}\n\t\t\t\t} catch (_e) {}\n\n\t\t\t\t// Handle socket transport downgrading when not offline.\n\t\t\t\tif (\n\t\t\t\t\tisWebSocketTransportError &&\n\t\t\t\t\tthis.enableLongPollingDowngrades &&\n\t\t\t\t\tthis.socket.io.opts.transports?.[0] !== \"polling\"\n\t\t\t\t) {\n\t\t\t\t\t// Downgrade transports to polling upgrade mechanism.\n\t\t\t\t\tthis.socket.io.opts.transports = [\"polling\", \"websocket\"];\n\t\t\t\t\t// Don't alter reconnection behavior if already enabled.\n\t\t\t\t\tif (!isInternalSocketReconnectionEnabled()) {\n\t\t\t\t\t\t// Allow single reconnection attempt using polling upgrade mechanism.\n\t\t\t\t\t\tthis.socket.io.reconnection(true);\n\t\t\t\t\t\tthis.socket.io.reconnectionAttempts(1);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Allow built-in socket.io reconnection handling.\n\t\t\t\tif (\n\t\t\t\t\tisInternalSocketReconnectionEnabled() &&\n\t\t\t\t\tinternalSocketConnectionFailureCount <\n\t\t\t\t\t\tgetMaxAllowedInternalSocketConnectionFailures()\n\t\t\t\t) {\n\t\t\t\t\t// Reconnection is enabled and maximum reconnect attempts have not been reached.\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tfailAndCloseSocket(this.createErrorObject(\"connect_error\", error));\n\t\t\t});\n\n\t\t\t// Listen for timeouts\n\t\t\tthis.addConnectionListener(\"connect_timeout\", () => {\n\t\t\t\tfailAndCloseSocket(this.createErrorObject(\"connect_timeout\"));\n\t\t\t});\n\n\t\t\tthis.addConnectionListener(\"connect_document_success\", (response: IConnected) => {\n\t\t\t\t// If we sent a nonce and the server supports nonces, check that the nonces match\n\t\t\t\tif (\n\t\t\t\t\tconnectMessage.nonce !== undefined &&\n\t\t\t\t\tresponse.nonce !== undefined &&\n\t\t\t\t\tresponse.nonce !== connectMessage.nonce\n\t\t\t\t) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst requestedMode = connectMessage.mode;\n\t\t\t\tconst actualMode = response.mode;\n\t\t\t\tconst writingPermitted = response.claims.scopes.includes(ScopeType.DocWrite);\n\n\t\t\t\tif (writingPermitted) {\n\t\t\t\t\t// The only time we expect a mismatch in requested/actual is if we lack write permissions\n\t\t\t\t\t// In this case we will get \"read\", even if we requested \"write\"\n\t\t\t\t\tif (actualMode !== requestedMode) {\n\t\t\t\t\t\tfailConnection(\n\t\t\t\t\t\t\tthis.createErrorObject(\n\t\t\t\t\t\t\t\t\"connect_document_success\",\n\t\t\t\t\t\t\t\t\"Connected in a different mode than was requested\",\n\t\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (actualMode === \"write\") {\n\t\t\t\t\t\tfailConnection(\n\t\t\t\t\t\t\tthis.createErrorObject(\n\t\t\t\t\t\t\t\t\"connect_document_success\",\n\t\t\t\t\t\t\t\t\"Connected in write mode without write permissions\",\n\t\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tthis.logger.sendTelemetryEvent(\n\t\t\t\t\t{\n\t\t\t\t\t\teventName: \"ConnectDocumentSuccess\",\n\t\t\t\t\t\tpendingClientId: response.clientId,\n\t\t\t\t\t},\n\t\t\t\t\tundefined,\n\t\t\t\t\tLogLevel.verbose,\n\t\t\t\t);\n\n\t\t\t\tthis.checkpointSequenceNumber = response.checkpointSequenceNumber;\n\n\t\t\t\tthis.removeConnectionListeners();\n\t\t\t\tresolve(response);\n\t\t\t});\n\n\t\t\t// Socket can be disconnected while waiting for Fluid protocol messages\n\t\t\t// (connect_document_error / connect_document_success), as well as before DeltaManager\n\t\t\t// had a chance to register its handlers.\n\t\t\tthis.addTrackedListener(\"disconnect\", (reason, details) => {\n\t\t\t\tfailAndCloseSocket(\n\t\t\t\t\tthis.createErrorObjectWithProps(\"disconnect\", reason, {\n\t\t\t\t\t\tsocketErrorType: details?.context?.type,\n\t\t\t\t\t\t// https://www.rfc-editor.org/rfc/rfc6455#section-7.4\n\t\t\t\t\t\tsocketCode: details?.context?.code,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t});\n\n\t\t\tthis.addTrackedListener(\"error\", (error) => {\n\t\t\t\t// This includes \"Invalid namespace\" error, which we consider critical (reconnecting will not help)\n\t\t\t\tconst err = this.createErrorObject(\"error\", error, error !== \"Invalid namespace\");\n\t\t\t\t// Disconnect socket - required if happened before initial handshake\n\t\t\t\tfailAndCloseSocket(err);\n\t\t\t});\n\n\t\t\tthis.addConnectionListener(\"connect_document_error\", (error) => {\n\t\t\t\t// If we sent a nonce and the server supports nonces, check that the nonces match\n\t\t\t\tif (\n\t\t\t\t\tconnectMessage.nonce !== undefined &&\n\t\t\t\t\terror.nonce !== undefined &&\n\t\t\t\t\terror.nonce !== connectMessage.nonce\n\t\t\t\t) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// This is not an socket.io error - it's Fluid protocol error.\n\t\t\t\t// In this case fail connection and indicate that we were unable to create connection\n\t\t\t\tfailConnection(this.createErrorObject(\"connect_document_error\", error));\n\t\t\t});\n\n\t\t\tthis.socket.emit(\"connect_document\", connectMessage);\n\t\t});\n\n\t\tassert(!this.disposed, 0x246 /* \"checking consistency of socket & _disposed flags\" */);\n\t}\n\n\tprivate addPropsToError(errorToBeNormalized: unknown) {\n\t\tconst normalizedError = normalizeError(errorToBeNormalized, {\n\t\t\tprops: {\n\t\t\t\tdetails: JSON.stringify({\n\t\t\t\t\t...this.getConnectionDetailsProps(),\n\t\t\t\t}),\n\t\t\t},\n\t\t});\n\t\treturn normalizedError;\n\t}\n\n\tprotected getConnectionDetailsProps() {\n\t\treturn {\n\t\t\tdisposed: this._disposed,\n\t\t\tsocketConnected: this.socket?.connected,\n\t\t\tclientId: this._details?.clientId,\n\t\t\tconnectionId: this.connectionId,\n\t\t};\n\t}\n\n\tprotected earlyOpHandler = (documentId: string, msgs: ISequencedDocumentMessage[]) => {\n\t\tthis.queuedMessages.push(...msgs);\n\t};\n\n\tprotected earlySignalHandler = (msg: ISignalMessage | ISignalMessage[]) => {\n\t\tif (Array.isArray(msg)) {\n\t\t\tthis.queuedSignals.push(...msg);\n\t\t} else {\n\t\t\tthis.queuedSignals.push(msg);\n\t\t}\n\t};\n\n\tprivate removeEarlyOpHandler() {\n\t\tthis.socket.removeListener(\"op\", this.earlyOpHandler);\n\t\tthis.earlyOpHandlerAttached = false;\n\t}\n\n\tprivate removeEarlySignalHandler() {\n\t\tthis.socket.removeListener(\"signal\", this.earlySignalHandler);\n\t}\n\n\tprivate addConnectionListener(event: string, listener: (...args: any[]) => void) {\n\t\tassert(\n\t\t\t!DocumentDeltaConnection.eventsAlwaysForwarded.includes(event),\n\t\t\t0x247 /* \"Use addTrackedListener instead\" */,\n\t\t);\n\t\tassert(\n\t\t\t!DocumentDeltaConnection.eventsToForward.includes(event),\n\t\t\t0x248 /* \"should not subscribe to forwarded events\" */,\n\t\t);\n\t\tthis.socket.on(event, listener);\n\t\tassert(!this.connectionListeners.has(event), 0x20d /* \"double connection listener\" */);\n\t\tthis.connectionListeners.set(event, listener);\n\t}\n\n\tprotected addTrackedListener(event: string, listener: (...args: any[]) => void) {\n\t\tthis.socket.on(event, listener);\n\t\tassert(!this.trackedListeners.has(event), 0x20e /* \"double tracked listener\" */);\n\t\tthis.trackedListeners.set(event, listener);\n\t}\n\n\tprivate removeTrackedListeners() {\n\t\tfor (const [event, listener] of this.trackedListeners.entries()) {\n\t\t\tthis.socket.off(event, listener);\n\t\t}\n\t\t// removeTrackedListeners removes all listeners, including connection listeners\n\t\tthis.removeConnectionListeners();\n\n\t\tthis.removeEarlyOpHandler();\n\t\tthis.removeEarlySignalHandler();\n\n\t\tthis.trackedListeners.clear();\n\t}\n\n\tprivate removeConnectionListeners() {\n\t\tif (this.socketConnectionTimeout !== undefined) {\n\t\t\tclearTimeout(this.socketConnectionTimeout);\n\t\t}\n\n\t\tfor (const [event, listener] of this.connectionListeners.entries()) {\n\t\t\tthis.socket.off(event, listener);\n\t\t}\n\t\tthis.connectionListeners.clear();\n\t}\n\n\tprivate getErrorMessage(error?: any): string {\n\t\tif (error?.type !== \"TransportError\") {\n\t\t\treturn extractLogSafeErrorProperties(error, true).message;\n\t\t}\n\t\t// JSON.stringify drops Error.message\n\t\tconst messagePrefix = error?.message !== undefined ? `${error.message}: ` : \"\";\n\n\t\t// Websocket errors reported by engine.io-client.\n\t\t// They are Error objects with description containing WS error and description = \"TransportError\"\n\t\t// Please see https://github.com/socketio/engine.io-client/blob/7245b80/lib/transport.ts#L44,\n\t\treturn `${messagePrefix}${JSON.stringify(error, getCircularReplacer())}`;\n\t}\n\n\tprivate createErrorObjectWithProps(\n\t\thandler: string,\n\t\terror?: any,\n\t\tprops?: ITelemetryProperties,\n\t\tcanRetry = true,\n\t): IAnyDriverError {\n\t\treturn createGenericNetworkError(\n\t\t\t`socket.io (${handler}): ${this.getErrorMessage(error)}`,\n\t\t\t{ canRetry },\n\t\t\t{\n\t\t\t\t...props,\n\t\t\t\tdriverVersion,\n\t\t\t\tdetails: JSON.stringify({\n\t\t\t\t\t...this.getConnectionDetailsProps(),\n\t\t\t\t}),\n\t\t\t},\n\t\t);\n\t}\n\n\t/**\n\t * Error raising for socket.io issues\n\t */\n\tprotected createErrorObject(handler: string, error?: any, canRetry = true): IAnyDriverError {\n\t\treturn createGenericNetworkError(\n\t\t\t`socket.io (${handler}): ${this.getErrorMessage(error)}`,\n\t\t\t{ canRetry },\n\t\t\t{\n\t\t\t\tdriverVersion,\n\t\t\t\tdetails: JSON.stringify({\n\t\t\t\t\t...this.getConnectionDetailsProps(),\n\t\t\t\t}),\n\t\t\t},\n\t\t);\n\t}\n}\n"]}
|