@fluidframework/driver-base 1.4.0-121020 → 2.0.0-dev-rc.1.0.0.224419
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-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} +34 -18
- 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} +1 -1
- package/lib/{packageVersion.d.ts.map → packageVersion.d.mts.map} +1 -1
- package/lib/{packageVersion.js → packageVersion.mjs} +2 -2
- package/lib/packageVersion.mjs.map +1 -0
- package/package.json +113 -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.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
|
@@ -5,32 +5,55 @@
|
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.DocumentDeltaConnection = void 0;
|
|
8
|
-
const
|
|
8
|
+
const core_utils_1 = require("@fluidframework/core-utils");
|
|
9
9
|
const driver_utils_1 = require("@fluidframework/driver-utils");
|
|
10
10
|
const protocol_definitions_1 = require("@fluidframework/protocol-definitions");
|
|
11
|
+
const core_interfaces_1 = require("@fluidframework/core-interfaces");
|
|
11
12
|
const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
|
|
12
13
|
// For now, this package is versioned and released in unison with the specific drivers
|
|
13
|
-
const packageVersion_1 = require("./packageVersion");
|
|
14
|
+
const packageVersion_1 = require("./packageVersion.cjs");
|
|
14
15
|
/**
|
|
15
|
-
* Represents a connection to a stream of delta updates
|
|
16
|
+
* Represents a connection to a stream of delta updates.
|
|
17
|
+
* @internal
|
|
16
18
|
*/
|
|
17
19
|
class DocumentDeltaConnection extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
20
|
+
get hasDetails() {
|
|
21
|
+
return !!this._details;
|
|
22
|
+
}
|
|
23
|
+
get disposed() {
|
|
24
|
+
(0, core_utils_1.assert)(this._disposed || this.socket.connected, 0x244 /* "Socket is closed, but connection is not!" */);
|
|
25
|
+
return this._disposed;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* @deprecated Implementors should manage their own logger or monitoring context
|
|
29
|
+
*/
|
|
30
|
+
get logger() {
|
|
31
|
+
return this.mc.logger;
|
|
32
|
+
}
|
|
33
|
+
get details() {
|
|
34
|
+
if (!this._details) {
|
|
35
|
+
throw new Error("Internal error: calling method before _details is initialized!");
|
|
36
|
+
}
|
|
37
|
+
return this._details;
|
|
38
|
+
}
|
|
18
39
|
/**
|
|
19
40
|
* @param socket - websocket to be used
|
|
20
41
|
* @param documentId - ID of the document
|
|
21
42
|
* @param logger - for reporting telemetry events
|
|
22
43
|
* @param enableLongPollingDowngrades - allow connection to be downgraded to long-polling on websocket failure
|
|
23
44
|
*/
|
|
24
|
-
constructor(socket, documentId, logger, enableLongPollingDowngrades = false) {
|
|
45
|
+
constructor(socket, documentId, logger, enableLongPollingDowngrades = false, connectionId) {
|
|
25
46
|
super((name, error) => {
|
|
47
|
+
this.addPropsToError(error);
|
|
26
48
|
logger.sendErrorEvent({
|
|
27
49
|
eventName: "DeltaConnection:EventException",
|
|
28
|
-
name,
|
|
50
|
+
name: name,
|
|
29
51
|
}, error);
|
|
30
52
|
});
|
|
31
53
|
this.socket = socket;
|
|
32
54
|
this.documentId = documentId;
|
|
33
55
|
this.enableLongPollingDowngrades = enableLongPollingDowngrades;
|
|
56
|
+
this.connectionId = connectionId;
|
|
34
57
|
// Listen for ops sent before we receive a response to connect_document
|
|
35
58
|
this.queuedMessages = [];
|
|
36
59
|
this.queuedSignals = [];
|
|
@@ -52,14 +75,19 @@ class DocumentDeltaConnection extends telemetry_utils_1.EventEmitterWithErrorHan
|
|
|
52
75
|
this.queuedMessages.push(...msgs);
|
|
53
76
|
};
|
|
54
77
|
this.earlySignalHandler = (msg) => {
|
|
55
|
-
|
|
78
|
+
if (Array.isArray(msg)) {
|
|
79
|
+
this.queuedSignals.push(...msg);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
this.queuedSignals.push(msg);
|
|
83
|
+
}
|
|
56
84
|
};
|
|
57
|
-
this.mc = (0, telemetry_utils_1.
|
|
58
|
-
this.on("newListener", (event,
|
|
59
|
-
(0,
|
|
85
|
+
this.mc = (0, telemetry_utils_1.createChildMonitoringContext)({ logger, namespace: "DeltaConnection" });
|
|
86
|
+
this.on("newListener", (event, _listener) => {
|
|
87
|
+
(0, core_utils_1.assert)(!this.disposed, 0x20a /* "register for event on disposed object" */);
|
|
60
88
|
// Some events are already forwarded - see this.addTrackedListener() calls in initialize().
|
|
61
89
|
if (DocumentDeltaConnection.eventsAlwaysForwarded.includes(event)) {
|
|
62
|
-
(0,
|
|
90
|
+
(0, core_utils_1.assert)(this.trackedListeners.has(event), 0x245 /* "tracked listener" */);
|
|
63
91
|
return;
|
|
64
92
|
}
|
|
65
93
|
if (!DocumentDeltaConnection.eventsToForward.includes(event)) {
|
|
@@ -70,33 +98,31 @@ class DocumentDeltaConnection extends telemetry_utils_1.EventEmitterWithErrorHan
|
|
|
70
98
|
// and that there are no "internal" listeners installed (like "error" case we skip above)
|
|
71
99
|
// Better flow might be to always unconditionally register all handlers on successful connection,
|
|
72
100
|
// though some logic (naming assert in initialMessages getter) might need to be adjusted (it becomes noop)
|
|
73
|
-
(0,
|
|
101
|
+
(0, core_utils_1.assert)((this.listeners(event).length !== 0) === this.trackedListeners.has(event), 0x20b /* "mismatch" */);
|
|
74
102
|
if (!this.trackedListeners.has(event)) {
|
|
75
|
-
|
|
76
|
-
this
|
|
77
|
-
|
|
103
|
+
if (event === "pong") {
|
|
104
|
+
// Empty callback for tracking purposes in this class
|
|
105
|
+
this.trackedListeners.set("pong", () => { });
|
|
106
|
+
const sendPingLoop = () => {
|
|
107
|
+
const start = Date.now();
|
|
108
|
+
this.socket.volatile?.emit("ping", () => {
|
|
109
|
+
this.emit("pong", Date.now() - start);
|
|
110
|
+
// Schedule another ping event in 1 minute
|
|
111
|
+
this.trackLatencyTimeout = setTimeout(() => {
|
|
112
|
+
sendPingLoop();
|
|
113
|
+
}, 1000 * 60);
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
sendPingLoop();
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
this.addTrackedListener(event, (...args) => {
|
|
120
|
+
this.emit(event, ...args);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
78
123
|
}
|
|
79
124
|
});
|
|
80
125
|
}
|
|
81
|
-
get hasDetails() {
|
|
82
|
-
return !!this._details;
|
|
83
|
-
}
|
|
84
|
-
get disposed() {
|
|
85
|
-
(0, common_utils_1.assert)(this._disposed || this.socket.connected, 0x244 /* "Socket is closed, but connection is not!" */);
|
|
86
|
-
return this._disposed;
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* @deprecated - Implementors should manage their own logger or monitoring context
|
|
90
|
-
*/
|
|
91
|
-
get logger() {
|
|
92
|
-
return this.mc.logger;
|
|
93
|
-
}
|
|
94
|
-
get details() {
|
|
95
|
-
if (!this._details) {
|
|
96
|
-
throw new Error("Internal error: calling method before _details is initialized!");
|
|
97
|
-
}
|
|
98
|
-
return this._details;
|
|
99
|
-
}
|
|
100
126
|
/**
|
|
101
127
|
* Get the ID of the client who is sending the message
|
|
102
128
|
*
|
|
@@ -149,8 +175,8 @@ class DocumentDeltaConnection extends telemetry_utils_1.EventEmitterWithErrorHan
|
|
|
149
175
|
get serviceConfiguration() {
|
|
150
176
|
return this.details.serviceConfiguration;
|
|
151
177
|
}
|
|
152
|
-
|
|
153
|
-
(0,
|
|
178
|
+
checkNotDisposed() {
|
|
179
|
+
(0, core_utils_1.assert)(!this.disposed, 0x20c /* "connection disposed" */);
|
|
154
180
|
}
|
|
155
181
|
/**
|
|
156
182
|
* Get messages sent during the connection
|
|
@@ -158,12 +184,12 @@ class DocumentDeltaConnection extends telemetry_utils_1.EventEmitterWithErrorHan
|
|
|
158
184
|
* @returns messages sent during the connection
|
|
159
185
|
*/
|
|
160
186
|
get initialMessages() {
|
|
161
|
-
this.
|
|
187
|
+
this.checkNotDisposed();
|
|
162
188
|
// If we call this when the earlyOpHandler is not attached, then the queuedMessages may not include the
|
|
163
189
|
// latest ops. This could possibly indicate that initialMessages was called twice.
|
|
164
|
-
(0,
|
|
190
|
+
(0, core_utils_1.assert)(this.earlyOpHandlerAttached, 0x08e /* "Potentially missed initial messages" */);
|
|
165
191
|
// We will lose ops and perf will tank as we need to go to storage to become current!
|
|
166
|
-
(0,
|
|
192
|
+
(0, core_utils_1.assert)(this.listeners("op").length !== 0, 0x08f /* "No op handler is setup!" */);
|
|
167
193
|
this.removeEarlyOpHandler();
|
|
168
194
|
if (this.queuedMessages.length > 0) {
|
|
169
195
|
// Some messages were queued.
|
|
@@ -180,8 +206,8 @@ class DocumentDeltaConnection extends telemetry_utils_1.EventEmitterWithErrorHan
|
|
|
180
206
|
* @returns signals sent during the connection
|
|
181
207
|
*/
|
|
182
208
|
get initialSignals() {
|
|
183
|
-
this.
|
|
184
|
-
(0,
|
|
209
|
+
this.checkNotDisposed();
|
|
210
|
+
(0, core_utils_1.assert)(this.listeners("signal").length !== 0, 0x090 /* "No signal handler is setup!" */);
|
|
185
211
|
this.removeEarlySignalHandler();
|
|
186
212
|
if (this.queuedSignals.length > 0) {
|
|
187
213
|
// Some signals were queued.
|
|
@@ -197,7 +223,7 @@ class DocumentDeltaConnection extends telemetry_utils_1.EventEmitterWithErrorHan
|
|
|
197
223
|
* @returns initial client list sent during the connection
|
|
198
224
|
*/
|
|
199
225
|
get initialClients() {
|
|
200
|
-
this.
|
|
226
|
+
this.checkNotDisposed();
|
|
201
227
|
return this.details.initialClients;
|
|
202
228
|
}
|
|
203
229
|
emitMessages(type, messages) {
|
|
@@ -208,59 +234,89 @@ class DocumentDeltaConnection extends telemetry_utils_1.EventEmitterWithErrorHan
|
|
|
208
234
|
this.socket.emit(type, this.clientId, messages);
|
|
209
235
|
}
|
|
210
236
|
}
|
|
211
|
-
submitCore(type, messages) {
|
|
212
|
-
this.emitMessages(type, [messages]);
|
|
213
|
-
}
|
|
214
237
|
/**
|
|
215
238
|
* Submits a new delta operation to the server
|
|
216
239
|
*
|
|
217
240
|
* @param message - delta operation to submit
|
|
218
241
|
*/
|
|
219
242
|
submit(messages) {
|
|
220
|
-
this.
|
|
221
|
-
this.
|
|
243
|
+
this.checkNotDisposed();
|
|
244
|
+
this.emitMessages("submitOp", [messages]);
|
|
222
245
|
}
|
|
223
246
|
/**
|
|
224
247
|
* Submits a new signal to the server
|
|
225
248
|
*
|
|
226
|
-
* @param
|
|
249
|
+
* @param content - Content of the signal.
|
|
250
|
+
* @param targetClientId - When specified, the signal is only sent to the provided client id.
|
|
251
|
+
*/
|
|
252
|
+
submitSignal(content, targetClientId) {
|
|
253
|
+
this.checkNotDisposed();
|
|
254
|
+
if (targetClientId && this.details.supportedFeatures?.submit_signals_v2 !== true) {
|
|
255
|
+
throw new driver_utils_1.UsageError("Sending signals to specific client ids is not supported.");
|
|
256
|
+
}
|
|
257
|
+
this.emitMessages("submitSignal", [[content]]);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Disconnect from the websocket and close the websocket too.
|
|
227
261
|
*/
|
|
228
|
-
|
|
229
|
-
this.
|
|
230
|
-
|
|
262
|
+
closeSocket(error) {
|
|
263
|
+
if (this._disposed) {
|
|
264
|
+
// This would be rare situation due to complexity around socket emitting events.
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
this.closeSocketCore(error);
|
|
268
|
+
}
|
|
269
|
+
closeSocketCore(error) {
|
|
270
|
+
this.disconnect(error);
|
|
231
271
|
}
|
|
232
272
|
/**
|
|
233
|
-
* Disconnect from the websocket, and permanently disable this DocumentDeltaConnection.
|
|
273
|
+
* Disconnect from the websocket, and permanently disable this DocumentDeltaConnection and close the socket.
|
|
274
|
+
* However the OdspDocumentDeltaConnection differ in dispose as in there we don't close the socket. There is no
|
|
275
|
+
* multiplexing here, so we need to close the socket here.
|
|
234
276
|
*/
|
|
235
277
|
dispose() {
|
|
236
|
-
this.
|
|
237
|
-
|
|
278
|
+
this.logger.sendTelemetryEvent({
|
|
279
|
+
eventName: "ClientClosingDeltaConnection",
|
|
280
|
+
driverVersion: packageVersion_1.pkgVersion,
|
|
281
|
+
details: JSON.stringify({
|
|
282
|
+
...this.getConnectionDetailsProps(),
|
|
283
|
+
}),
|
|
284
|
+
});
|
|
285
|
+
this.disconnect((0, driver_utils_1.createGenericNetworkError)(
|
|
238
286
|
// pre-0.58 error message: clientClosingConnection
|
|
239
287
|
"Client closing delta connection", { canRetry: true }, { driverVersion: packageVersion_1.pkgVersion }));
|
|
240
288
|
}
|
|
241
|
-
|
|
289
|
+
disconnect(err) {
|
|
242
290
|
// Can't check this.disposed here, as we get here on socket closure,
|
|
243
291
|
// so _disposed & socket.connected might be not in sync while processing
|
|
244
292
|
// "dispose" event.
|
|
245
293
|
if (this._disposed) {
|
|
246
294
|
return;
|
|
247
295
|
}
|
|
296
|
+
if (this.trackLatencyTimeout !== undefined) {
|
|
297
|
+
clearTimeout(this.trackLatencyTimeout);
|
|
298
|
+
this.trackLatencyTimeout = undefined;
|
|
299
|
+
}
|
|
248
300
|
// We set the disposed flag as a part of the contract for overriding the disconnect method. This is used by
|
|
249
301
|
// DocumentDeltaConnection to determine if emitting messages (ops) on the socket is allowed, which is
|
|
250
302
|
// important since OdspDocumentDeltaConnection reuses the socket rather than truly disconnecting it. Note that
|
|
251
303
|
// OdspDocumentDeltaConnection may still send disconnect_document which is allowed; this is only intended
|
|
252
304
|
// to prevent normal messages from being emitted.
|
|
253
305
|
this._disposed = true;
|
|
306
|
+
// Remove all listeners listening on the socket. These are listeners on socket and not on this connection
|
|
307
|
+
// object. Anyway since we have disposed this connection object, nobody should listen to event on socket
|
|
308
|
+
// anymore.
|
|
254
309
|
this.removeTrackedListeners();
|
|
255
|
-
|
|
310
|
+
// Clear the connection/socket before letting the deltaManager/connection manager know about the disconnect.
|
|
311
|
+
this.disconnectCore();
|
|
312
|
+
// Let user of connection object know about disconnect.
|
|
313
|
+
this.emit("disconnect", err);
|
|
256
314
|
}
|
|
257
315
|
/**
|
|
258
316
|
* Disconnect from the websocket.
|
|
259
|
-
* @param socketProtocolError - true if error happened on socket / socket.io protocol level
|
|
260
|
-
* (not on Fluid protocol level)
|
|
261
317
|
* @param reason - reason for disconnect
|
|
262
318
|
*/
|
|
263
|
-
|
|
319
|
+
disconnectCore() {
|
|
264
320
|
this.socket.disconnect();
|
|
265
321
|
}
|
|
266
322
|
async initialize(connectMessage, timeout) {
|
|
@@ -270,23 +326,56 @@ class DocumentDeltaConnection extends telemetry_utils_1.EventEmitterWithErrorHan
|
|
|
270
326
|
// Socket.io's reconnect_attempt event is unreliable, so we track connect_error count instead.
|
|
271
327
|
let internalSocketConnectionFailureCount = 0;
|
|
272
328
|
const isInternalSocketReconnectionEnabled = () => this.socket.io.reconnection();
|
|
273
|
-
const getMaxInternalSocketReconnectionAttempts = () => isInternalSocketReconnectionEnabled()
|
|
274
|
-
? this.socket.io.reconnectionAttempts()
|
|
275
|
-
: 0;
|
|
329
|
+
const getMaxInternalSocketReconnectionAttempts = () => isInternalSocketReconnectionEnabled() ? this.socket.io.reconnectionAttempts() : 0;
|
|
276
330
|
const getMaxAllowedInternalSocketConnectionFailures = () => getMaxInternalSocketReconnectionAttempts() + 1;
|
|
277
331
|
this._details = await new Promise((resolve, reject) => {
|
|
278
|
-
const
|
|
279
|
-
|
|
332
|
+
const failAndCloseSocket = (err) => {
|
|
333
|
+
try {
|
|
334
|
+
this.closeSocket(err);
|
|
335
|
+
}
|
|
336
|
+
catch (failError) {
|
|
337
|
+
const normalizedError = this.addPropsToError(failError);
|
|
338
|
+
this.logger.sendErrorEvent({ eventName: "CloseSocketError" }, normalizedError);
|
|
339
|
+
}
|
|
340
|
+
reject(err);
|
|
341
|
+
};
|
|
342
|
+
const failConnection = (err) => {
|
|
343
|
+
try {
|
|
344
|
+
this.disconnect(err);
|
|
345
|
+
}
|
|
346
|
+
catch (failError) {
|
|
347
|
+
const normalizedError = this.addPropsToError(failError);
|
|
348
|
+
this.logger.sendErrorEvent({ eventName: "FailConnectionError" }, normalizedError);
|
|
349
|
+
}
|
|
280
350
|
reject(err);
|
|
281
351
|
};
|
|
352
|
+
// Immediately set the connection timeout.
|
|
353
|
+
// Give extra 2 seconds for handshake on top of socket connection timeout.
|
|
354
|
+
this.socketConnectionTimeout = setTimeout(() => {
|
|
355
|
+
failConnection(this.createErrorObject("orderingServiceHandshakeTimeout"));
|
|
356
|
+
}, timeout + 2000);
|
|
282
357
|
// Listen for connection issues
|
|
283
358
|
this.addConnectionListener("connect_error", (error) => {
|
|
284
|
-
var _a;
|
|
285
359
|
internalSocketConnectionFailureCount++;
|
|
286
360
|
let isWebSocketTransportError = false;
|
|
287
361
|
try {
|
|
288
|
-
const description = error
|
|
289
|
-
|
|
362
|
+
const description = error?.description;
|
|
363
|
+
const context = error?.context;
|
|
364
|
+
if (context && typeof context === "object") {
|
|
365
|
+
const statusText = context.statusText?.code;
|
|
366
|
+
// Self-Signed Certificate ErrorCode Found in error.context
|
|
367
|
+
if (statusText === "DEPTH_ZERO_SELF_SIGNED_CERT") {
|
|
368
|
+
failAndCloseSocket(this.createErrorObject("connect_error", error, false));
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
else if (description && typeof description === "object") {
|
|
373
|
+
const errorCode = description.error?.code;
|
|
374
|
+
// Self-Signed Certificate ErrorCode Found in error.description
|
|
375
|
+
if (errorCode === "DEPTH_ZERO_SELF_SIGNED_CERT") {
|
|
376
|
+
failAndCloseSocket(this.createErrorObject("connect_error", error, false));
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
290
379
|
if (error.type === "TransportError") {
|
|
291
380
|
isWebSocketTransportError = true;
|
|
292
381
|
}
|
|
@@ -298,7 +387,7 @@ class DocumentDeltaConnection extends telemetry_utils_1.EventEmitterWithErrorHan
|
|
|
298
387
|
// Handle socket transport downgrading when not offline.
|
|
299
388
|
if (isWebSocketTransportError &&
|
|
300
389
|
this.enableLongPollingDowngrades &&
|
|
301
|
-
|
|
390
|
+
this.socket.io.opts.transports?.[0] !== "polling") {
|
|
302
391
|
// Downgrade transports to polling upgrade mechanism.
|
|
303
392
|
this.socket.io.opts.transports = ["polling", "websocket"];
|
|
304
393
|
// Don't alter reconnection behavior if already enabled.
|
|
@@ -310,15 +399,16 @@ class DocumentDeltaConnection extends telemetry_utils_1.EventEmitterWithErrorHan
|
|
|
310
399
|
}
|
|
311
400
|
// Allow built-in socket.io reconnection handling.
|
|
312
401
|
if (isInternalSocketReconnectionEnabled() &&
|
|
313
|
-
internalSocketConnectionFailureCount <
|
|
402
|
+
internalSocketConnectionFailureCount <
|
|
403
|
+
getMaxAllowedInternalSocketConnectionFailures()) {
|
|
314
404
|
// Reconnection is enabled and maximum reconnect attempts have not been reached.
|
|
315
405
|
return;
|
|
316
406
|
}
|
|
317
|
-
|
|
407
|
+
failAndCloseSocket(this.createErrorObject("connect_error", error));
|
|
318
408
|
});
|
|
319
409
|
// Listen for timeouts
|
|
320
410
|
this.addConnectionListener("connect_timeout", () => {
|
|
321
|
-
|
|
411
|
+
failAndCloseSocket(this.createErrorObject("connect_timeout"));
|
|
322
412
|
});
|
|
323
413
|
this.addConnectionListener("connect_document_success", (response) => {
|
|
324
414
|
// If we sent a nonce and the server supports nonces, check that the nonces match
|
|
@@ -334,16 +424,20 @@ class DocumentDeltaConnection extends telemetry_utils_1.EventEmitterWithErrorHan
|
|
|
334
424
|
// The only time we expect a mismatch in requested/actual is if we lack write permissions
|
|
335
425
|
// In this case we will get "read", even if we requested "write"
|
|
336
426
|
if (actualMode !== requestedMode) {
|
|
337
|
-
|
|
427
|
+
failConnection(this.createErrorObject("connect_document_success", "Connected in a different mode than was requested", false));
|
|
338
428
|
return;
|
|
339
429
|
}
|
|
340
430
|
}
|
|
341
431
|
else {
|
|
342
432
|
if (actualMode === "write") {
|
|
343
|
-
|
|
433
|
+
failConnection(this.createErrorObject("connect_document_success", "Connected in write mode without write permissions", false));
|
|
344
434
|
return;
|
|
345
435
|
}
|
|
346
436
|
}
|
|
437
|
+
this.logger.sendTelemetryEvent({
|
|
438
|
+
eventName: "ConnectDocumentSuccess",
|
|
439
|
+
pendingClientId: response.clientId,
|
|
440
|
+
}, undefined, core_interfaces_1.LogLevel.verbose);
|
|
347
441
|
this.checkpointSequenceNumber = response.checkpointSequenceNumber;
|
|
348
442
|
this.removeConnectionListeners();
|
|
349
443
|
resolve(response);
|
|
@@ -351,20 +445,20 @@ class DocumentDeltaConnection extends telemetry_utils_1.EventEmitterWithErrorHan
|
|
|
351
445
|
// Socket can be disconnected while waiting for Fluid protocol messages
|
|
352
446
|
// (connect_document_error / connect_document_success), as well as before DeltaManager
|
|
353
447
|
// had a chance to register its handlers.
|
|
354
|
-
this.addTrackedListener("disconnect", (reason) => {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
448
|
+
this.addTrackedListener("disconnect", (reason, details) => {
|
|
449
|
+
failAndCloseSocket(this.createErrorObjectWithProps("disconnect", reason, {
|
|
450
|
+
socketErrorType: details?.context?.type,
|
|
451
|
+
// https://www.rfc-editor.org/rfc/rfc6455#section-7.4
|
|
452
|
+
socketCode: details?.context?.code,
|
|
453
|
+
}));
|
|
358
454
|
});
|
|
359
|
-
this.addTrackedListener("error", (
|
|
360
|
-
// First, raise an error event, to give clients a chance to observe error contents
|
|
455
|
+
this.addTrackedListener("error", (error) => {
|
|
361
456
|
// This includes "Invalid namespace" error, which we consider critical (reconnecting will not help)
|
|
362
457
|
const err = this.createErrorObject("error", error, error !== "Invalid namespace");
|
|
363
|
-
this.emit("error", err);
|
|
364
458
|
// Disconnect socket - required if happened before initial handshake
|
|
365
|
-
|
|
366
|
-
})
|
|
367
|
-
this.addConnectionListener("connect_document_error", (
|
|
459
|
+
failAndCloseSocket(err);
|
|
460
|
+
});
|
|
461
|
+
this.addConnectionListener("connect_document_error", (error) => {
|
|
368
462
|
// If we sent a nonce and the server supports nonces, check that the nonces match
|
|
369
463
|
if (connectMessage.nonce !== undefined &&
|
|
370
464
|
error.nonce !== undefined &&
|
|
@@ -373,15 +467,29 @@ class DocumentDeltaConnection extends telemetry_utils_1.EventEmitterWithErrorHan
|
|
|
373
467
|
}
|
|
374
468
|
// This is not an socket.io error - it's Fluid protocol error.
|
|
375
469
|
// In this case fail connection and indicate that we were unable to create connection
|
|
376
|
-
|
|
377
|
-
})
|
|
470
|
+
failConnection(this.createErrorObject("connect_document_error", error));
|
|
471
|
+
});
|
|
378
472
|
this.socket.emit("connect_document", connectMessage);
|
|
379
|
-
// Give extra 2 seconds for handshake on top of socket connection timeout
|
|
380
|
-
this.socketConnectionTimeout = setTimeout(() => {
|
|
381
|
-
fail(false, this.createErrorObject("orderingServiceHandshakeTimeout"));
|
|
382
|
-
}, timeout + 2000);
|
|
383
473
|
});
|
|
384
|
-
(0,
|
|
474
|
+
(0, core_utils_1.assert)(!this.disposed, 0x246 /* "checking consistency of socket & _disposed flags" */);
|
|
475
|
+
}
|
|
476
|
+
addPropsToError(errorToBeNormalized) {
|
|
477
|
+
const normalizedError = (0, telemetry_utils_1.normalizeError)(errorToBeNormalized, {
|
|
478
|
+
props: {
|
|
479
|
+
details: JSON.stringify({
|
|
480
|
+
...this.getConnectionDetailsProps(),
|
|
481
|
+
}),
|
|
482
|
+
},
|
|
483
|
+
});
|
|
484
|
+
return normalizedError;
|
|
485
|
+
}
|
|
486
|
+
getConnectionDetailsProps() {
|
|
487
|
+
return {
|
|
488
|
+
disposed: this._disposed,
|
|
489
|
+
socketConnected: this.socket?.connected,
|
|
490
|
+
clientId: this._details?.clientId,
|
|
491
|
+
connectionId: this.connectionId,
|
|
492
|
+
};
|
|
385
493
|
}
|
|
386
494
|
removeEarlyOpHandler() {
|
|
387
495
|
this.socket.removeListener("op", this.earlyOpHandler);
|
|
@@ -391,15 +499,15 @@ class DocumentDeltaConnection extends telemetry_utils_1.EventEmitterWithErrorHan
|
|
|
391
499
|
this.socket.removeListener("signal", this.earlySignalHandler);
|
|
392
500
|
}
|
|
393
501
|
addConnectionListener(event, listener) {
|
|
394
|
-
(0,
|
|
395
|
-
(0,
|
|
502
|
+
(0, core_utils_1.assert)(!DocumentDeltaConnection.eventsAlwaysForwarded.includes(event), 0x247 /* "Use addTrackedListener instead" */);
|
|
503
|
+
(0, core_utils_1.assert)(!DocumentDeltaConnection.eventsToForward.includes(event), 0x248 /* "should not subscribe to forwarded events" */);
|
|
396
504
|
this.socket.on(event, listener);
|
|
397
|
-
(0,
|
|
505
|
+
(0, core_utils_1.assert)(!this.connectionListeners.has(event), 0x20d /* "double connection listener" */);
|
|
398
506
|
this.connectionListeners.set(event, listener);
|
|
399
507
|
}
|
|
400
508
|
addTrackedListener(event, listener) {
|
|
401
509
|
this.socket.on(event, listener);
|
|
402
|
-
(0,
|
|
510
|
+
(0, core_utils_1.assert)(!this.trackedListeners.has(event), 0x20e /* "double tracked listener" */);
|
|
403
511
|
this.trackedListeners.set(event, listener);
|
|
404
512
|
}
|
|
405
513
|
removeTrackedListeners() {
|
|
@@ -421,29 +529,36 @@ class DocumentDeltaConnection extends telemetry_utils_1.EventEmitterWithErrorHan
|
|
|
421
529
|
}
|
|
422
530
|
this.connectionListeners.clear();
|
|
423
531
|
}
|
|
532
|
+
getErrorMessage(error) {
|
|
533
|
+
if (error?.type !== "TransportError") {
|
|
534
|
+
return (0, telemetry_utils_1.extractLogSafeErrorProperties)(error, true).message;
|
|
535
|
+
}
|
|
536
|
+
// JSON.stringify drops Error.message
|
|
537
|
+
const messagePrefix = error?.message !== undefined ? `${error.message}: ` : "";
|
|
538
|
+
// Websocket errors reported by engine.io-client.
|
|
539
|
+
// They are Error objects with description containing WS error and description = "TransportError"
|
|
540
|
+
// Please see https://github.com/socketio/engine.io-client/blob/7245b80/lib/transport.ts#L44,
|
|
541
|
+
return `${messagePrefix}${JSON.stringify(error, (0, telemetry_utils_1.getCircularReplacer)())}`;
|
|
542
|
+
}
|
|
543
|
+
createErrorObjectWithProps(handler, error, props, canRetry = true) {
|
|
544
|
+
return (0, driver_utils_1.createGenericNetworkError)(`socket.io (${handler}): ${this.getErrorMessage(error)}`, { canRetry }, {
|
|
545
|
+
...props,
|
|
546
|
+
driverVersion: packageVersion_1.pkgVersion,
|
|
547
|
+
details: JSON.stringify({
|
|
548
|
+
...this.getConnectionDetailsProps(),
|
|
549
|
+
}),
|
|
550
|
+
});
|
|
551
|
+
}
|
|
424
552
|
/**
|
|
425
553
|
* Error raising for socket.io issues
|
|
426
554
|
*/
|
|
427
555
|
createErrorObject(handler, error, canRetry = true) {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
const messagePrefix = ((error === null || error === void 0 ? void 0 : error.message) !== undefined)
|
|
435
|
-
? `${error.message}: `
|
|
436
|
-
: "";
|
|
437
|
-
// Websocket errors reported by engine.io-client.
|
|
438
|
-
// They are Error objects with description containing WS error and description = "TransportError"
|
|
439
|
-
// Please see https://github.com/socketio/engine.io-client/blob/7245b80/lib/transport.ts#L44,
|
|
440
|
-
message = `${messagePrefix}${JSON.stringify(error, (0, telemetry_utils_1.getCircularReplacer)())}`;
|
|
441
|
-
}
|
|
442
|
-
else {
|
|
443
|
-
message = (0, common_utils_1.extractLogSafeErrorProperties)(error).message;
|
|
444
|
-
}
|
|
445
|
-
const errorObj = (0, driver_utils_1.createGenericNetworkError)(`socket.io (${handler}): ${message}`, { canRetry }, { driverVersion: packageVersion_1.pkgVersion });
|
|
446
|
-
return errorObj;
|
|
556
|
+
return (0, driver_utils_1.createGenericNetworkError)(`socket.io (${handler}): ${this.getErrorMessage(error)}`, { canRetry }, {
|
|
557
|
+
driverVersion: packageVersion_1.pkgVersion,
|
|
558
|
+
details: JSON.stringify({
|
|
559
|
+
...this.getConnectionDetailsProps(),
|
|
560
|
+
}),
|
|
561
|
+
});
|
|
447
562
|
}
|
|
448
563
|
}
|
|
449
564
|
exports.DocumentDeltaConnection = DocumentDeltaConnection;
|
|
@@ -451,4 +566,4 @@ DocumentDeltaConnection.eventsToForward = ["nack", "op", "signal", "pong"];
|
|
|
451
566
|
// WARNING: These are critical events that we can't miss, so registration for them has to be in place at all times!
|
|
452
567
|
// Including before handshake is over, and after that (but before DeltaManager had a chance to put its own handlers)
|
|
453
568
|
DocumentDeltaConnection.eventsAlwaysForwarded = ["disconnect", "error"];
|
|
454
|
-
//# sourceMappingURL=documentDeltaConnection.
|
|
569
|
+
//# sourceMappingURL=documentDeltaConnection.cjs.map
|