@fluidframework/odsp-driver 2.0.0-internal.2.2.0 → 2.0.0-internal.2.3.0

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.
Files changed (149) hide show
  1. package/.eslintrc.js +1 -1
  2. package/dist/WriteBufferUtils.d.ts.map +1 -1
  3. package/dist/WriteBufferUtils.js +0 -1
  4. package/dist/WriteBufferUtils.js.map +1 -1
  5. package/dist/contractsPublic.d.ts.map +1 -1
  6. package/dist/contractsPublic.js.map +1 -1
  7. package/dist/createFile.d.ts +2 -6
  8. package/dist/createFile.d.ts.map +1 -1
  9. package/dist/createFile.js +13 -125
  10. package/dist/createFile.js.map +1 -1
  11. package/dist/createNewUtils.d.ts +16 -0
  12. package/dist/createNewUtils.d.ts.map +1 -1
  13. package/dist/createNewUtils.js +131 -1
  14. package/dist/createNewUtils.js.map +1 -1
  15. package/dist/fetchSnapshot.js +1 -1
  16. package/dist/fetchSnapshot.js.map +1 -1
  17. package/dist/localOdspDriver/localOdspDocumentServiceFactory.d.ts.map +1 -1
  18. package/dist/localOdspDriver/localOdspDocumentServiceFactory.js +1 -1
  19. package/dist/localOdspDriver/localOdspDocumentServiceFactory.js.map +1 -1
  20. package/dist/odspDelayLoadedDeltaStream.d.ts +75 -0
  21. package/dist/odspDelayLoadedDeltaStream.d.ts.map +1 -0
  22. package/dist/odspDelayLoadedDeltaStream.js +259 -0
  23. package/dist/odspDelayLoadedDeltaStream.js.map +1 -0
  24. package/dist/odspDocumentDeltaConnection.d.ts +1 -3
  25. package/dist/odspDocumentDeltaConnection.d.ts.map +1 -1
  26. package/dist/odspDocumentDeltaConnection.js +8 -7
  27. package/dist/odspDocumentDeltaConnection.js.map +1 -1
  28. package/dist/odspDocumentService.d.ts +10 -25
  29. package/dist/odspDocumentService.d.ts.map +1 -1
  30. package/dist/odspDocumentService.js +71 -204
  31. package/dist/odspDocumentService.js.map +1 -1
  32. package/dist/odspDocumentServiceFactory.d.ts.map +1 -1
  33. package/dist/odspDocumentServiceFactory.js +1 -2
  34. package/dist/odspDocumentServiceFactory.js.map +1 -1
  35. package/dist/odspDocumentServiceFactoryCore.d.ts +1 -3
  36. package/dist/odspDocumentServiceFactoryCore.d.ts.map +1 -1
  37. package/dist/odspDocumentServiceFactoryCore.js +3 -3
  38. package/dist/odspDocumentServiceFactoryCore.js.map +1 -1
  39. package/dist/odspDocumentServiceFactoryWithCodeSplit.d.ts +4 -0
  40. package/dist/odspDocumentServiceFactoryWithCodeSplit.d.ts.map +1 -1
  41. package/dist/odspDocumentServiceFactoryWithCodeSplit.js +5 -20
  42. package/dist/odspDocumentServiceFactoryWithCodeSplit.js.map +1 -1
  43. package/dist/odspDocumentStorageManager.d.ts.map +1 -1
  44. package/dist/odspDocumentStorageManager.js +0 -2
  45. package/dist/odspDocumentStorageManager.js.map +1 -1
  46. package/dist/odspDocumentStorageServiceBase.d.ts.map +1 -1
  47. package/dist/odspDocumentStorageServiceBase.js +0 -2
  48. package/dist/odspDocumentStorageServiceBase.js.map +1 -1
  49. package/dist/odspSummaryUploadManager.d.ts.map +1 -1
  50. package/dist/odspSummaryUploadManager.js +0 -2
  51. package/dist/odspSummaryUploadManager.js.map +1 -1
  52. package/dist/odspUtils.d.ts +10 -1
  53. package/dist/odspUtils.d.ts.map +1 -1
  54. package/dist/odspUtils.js +5 -1
  55. package/dist/odspUtils.js.map +1 -1
  56. package/dist/packageVersion.d.ts +1 -1
  57. package/dist/packageVersion.js +1 -1
  58. package/dist/packageVersion.js.map +1 -1
  59. package/dist/zipItDataRepresentationUtils.d.ts.map +1 -1
  60. package/dist/zipItDataRepresentationUtils.js +0 -1
  61. package/dist/zipItDataRepresentationUtils.js.map +1 -1
  62. package/lib/WriteBufferUtils.d.ts.map +1 -1
  63. package/lib/WriteBufferUtils.js +0 -1
  64. package/lib/WriteBufferUtils.js.map +1 -1
  65. package/lib/contractsPublic.d.ts.map +1 -1
  66. package/lib/contractsPublic.js.map +1 -1
  67. package/lib/createFile.d.ts +2 -6
  68. package/lib/createFile.d.ts.map +1 -1
  69. package/lib/createFile.js +15 -126
  70. package/lib/createFile.js.map +1 -1
  71. package/lib/createNewUtils.d.ts +16 -0
  72. package/lib/createNewUtils.d.ts.map +1 -1
  73. package/lib/createNewUtils.js +129 -1
  74. package/lib/createNewUtils.js.map +1 -1
  75. package/lib/fetchSnapshot.js +1 -1
  76. package/lib/fetchSnapshot.js.map +1 -1
  77. package/lib/localOdspDriver/localOdspDocumentServiceFactory.d.ts.map +1 -1
  78. package/lib/localOdspDriver/localOdspDocumentServiceFactory.js +1 -1
  79. package/lib/localOdspDriver/localOdspDocumentServiceFactory.js.map +1 -1
  80. package/lib/odspDelayLoadedDeltaStream.d.ts +75 -0
  81. package/lib/odspDelayLoadedDeltaStream.d.ts.map +1 -0
  82. package/lib/odspDelayLoadedDeltaStream.js +255 -0
  83. package/lib/odspDelayLoadedDeltaStream.js.map +1 -0
  84. package/lib/odspDocumentDeltaConnection.d.ts +1 -3
  85. package/lib/odspDocumentDeltaConnection.d.ts.map +1 -1
  86. package/lib/odspDocumentDeltaConnection.js +8 -7
  87. package/lib/odspDocumentDeltaConnection.js.map +1 -1
  88. package/lib/odspDocumentService.d.ts +10 -25
  89. package/lib/odspDocumentService.d.ts.map +1 -1
  90. package/lib/odspDocumentService.js +56 -207
  91. package/lib/odspDocumentService.js.map +1 -1
  92. package/lib/odspDocumentServiceFactory.d.ts.map +1 -1
  93. package/lib/odspDocumentServiceFactory.js +1 -2
  94. package/lib/odspDocumentServiceFactory.js.map +1 -1
  95. package/lib/odspDocumentServiceFactoryCore.d.ts +1 -3
  96. package/lib/odspDocumentServiceFactoryCore.d.ts.map +1 -1
  97. package/lib/odspDocumentServiceFactoryCore.js +3 -3
  98. package/lib/odspDocumentServiceFactoryCore.js.map +1 -1
  99. package/lib/odspDocumentServiceFactoryWithCodeSplit.d.ts +4 -0
  100. package/lib/odspDocumentServiceFactoryWithCodeSplit.d.ts.map +1 -1
  101. package/lib/odspDocumentServiceFactoryWithCodeSplit.js +5 -1
  102. package/lib/odspDocumentServiceFactoryWithCodeSplit.js.map +1 -1
  103. package/lib/odspDocumentStorageManager.d.ts.map +1 -1
  104. package/lib/odspDocumentStorageManager.js +0 -2
  105. package/lib/odspDocumentStorageManager.js.map +1 -1
  106. package/lib/odspDocumentStorageServiceBase.d.ts.map +1 -1
  107. package/lib/odspDocumentStorageServiceBase.js +0 -2
  108. package/lib/odspDocumentStorageServiceBase.js.map +1 -1
  109. package/lib/odspSummaryUploadManager.d.ts.map +1 -1
  110. package/lib/odspSummaryUploadManager.js +0 -2
  111. package/lib/odspSummaryUploadManager.js.map +1 -1
  112. package/lib/odspUtils.d.ts +10 -1
  113. package/lib/odspUtils.d.ts.map +1 -1
  114. package/lib/odspUtils.js +3 -0
  115. package/lib/odspUtils.js.map +1 -1
  116. package/lib/packageVersion.d.ts +1 -1
  117. package/lib/packageVersion.js +1 -1
  118. package/lib/packageVersion.js.map +1 -1
  119. package/lib/zipItDataRepresentationUtils.d.ts.map +1 -1
  120. package/lib/zipItDataRepresentationUtils.js +0 -1
  121. package/lib/zipItDataRepresentationUtils.js.map +1 -1
  122. package/package.json +14 -14
  123. package/src/WriteBufferUtils.ts +0 -1
  124. package/src/contractsPublic.ts +0 -1
  125. package/src/createFile.ts +23 -168
  126. package/src/createNewUtils.ts +188 -2
  127. package/src/fetchSnapshot.ts +1 -1
  128. package/src/localOdspDriver/localOdspDocumentServiceFactory.ts +0 -1
  129. package/src/odspDelayLoadedDeltaStream.ts +359 -0
  130. package/src/odspDocumentDeltaConnection.ts +11 -8
  131. package/src/odspDocumentService.ts +63 -283
  132. package/src/odspDocumentServiceFactory.ts +0 -2
  133. package/src/odspDocumentServiceFactoryCore.ts +1 -3
  134. package/src/odspDocumentServiceFactoryWithCodeSplit.ts +4 -1
  135. package/src/odspDocumentStorageManager.ts +0 -4
  136. package/src/odspDocumentStorageServiceBase.ts +0 -3
  137. package/src/odspSummaryUploadManager.ts +0 -4
  138. package/src/odspUtils.ts +16 -1
  139. package/src/packageVersion.ts +1 -1
  140. package/src/zipItDataRepresentationUtils.ts +0 -1
  141. package/dist/getSocketIo.d.ts +0 -11
  142. package/dist/getSocketIo.d.ts.map +0 -1
  143. package/dist/getSocketIo.js +0 -20
  144. package/dist/getSocketIo.js.map +0 -1
  145. package/lib/getSocketIo.d.ts +0 -11
  146. package/lib/getSocketIo.d.ts.map +0 -1
  147. package/lib/getSocketIo.js +0 -13
  148. package/lib/getSocketIo.js.map +0 -1
  149. package/src/getSocketIo.ts +0 -14
@@ -0,0 +1,255 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { assert, performance } from "@fluidframework/common-utils";
6
+ import { normalizeError, } from "@fluidframework/telemetry-utils";
7
+ import { DriverErrorType, } from "@fluidframework/driver-definitions";
8
+ import { canRetryOnError, DeltaStreamConnectionForbiddenError, NonRetryableError } from "@fluidframework/driver-utils";
9
+ import { OdspErrorType, } from "@fluidframework/odsp-driver-definitions";
10
+ import { hasFacetCodes } from "@fluidframework/odsp-doclib-utils";
11
+ import { OdspDocumentDeltaConnection } from "./odspDocumentDeltaConnection";
12
+ import { getWithRetryForTokenRefresh } from "./odspUtils";
13
+ import { fetchJoinSession } from "./vroom";
14
+ import { pkgVersion as driverVersion } from "./packageVersion";
15
+ /**
16
+ * This OdspDelayLoadedDeltaStream is used by OdspDocumentService.ts to delay load the delta connection
17
+ * as they are not on critical path of loading a container.
18
+ */
19
+ export class OdspDelayLoadedDeltaStream {
20
+ /**
21
+ * @param odspResolvedUrl - resolved url identifying document that will be managed by this service instance.
22
+ * @param policies - Document service policies.
23
+ * @param getStorageToken - function that can provide the storage token. This is is also referred to as
24
+ * the "Vroom" token in SPO.
25
+ * @param getWebsocketToken - function that can provide a token for accessing the web socket. This is also referred
26
+ * to as the "Push" token in SPO. If undefined then websocket token is expected to be returned with joinSession
27
+ * response payload.
28
+ * @param mc - a logger that can capture performance and diagnostic information
29
+ * @param cache - This caches response for joinSession.
30
+ * @param hostPolicy - host constructed policy which customizes service behavior.
31
+ * @param epochTracker - This helper class which adds epoch to backend calls made by this service instance.
32
+ * @param opsReceived - To register the ops received through socket.
33
+ * @param socketReferenceKeyPrefix - (optional) prefix to isolate socket reuse cache
34
+ */
35
+ constructor(odspResolvedUrl, policies, getStorageToken, getWebsocketToken, mc, cache, hostPolicy, epochTracker, opsReceived, socketReferenceKeyPrefix) {
36
+ this.odspResolvedUrl = odspResolvedUrl;
37
+ this.policies = policies;
38
+ this.getStorageToken = getStorageToken;
39
+ this.getWebsocketToken = getWebsocketToken;
40
+ this.mc = mc;
41
+ this.cache = cache;
42
+ this.hostPolicy = hostPolicy;
43
+ this.epochTracker = epochTracker;
44
+ this.opsReceived = opsReceived;
45
+ this.socketReferenceKeyPrefix = socketReferenceKeyPrefix;
46
+ this.joinSessionKey = `${this.odspResolvedUrl.hashedDocumentId}/joinsession`;
47
+ }
48
+ get resolvedUrl() {
49
+ return this.odspResolvedUrl;
50
+ }
51
+ get currentDeltaConnection() {
52
+ return this.currentConnection;
53
+ }
54
+ get relayServiceTenantAndSessionId() {
55
+ return this._relayServiceTenantAndSessionId;
56
+ }
57
+ /** Annotate the given error indicating which connection step failed */
58
+ annotateConnectionError(error, failedConnectionStep, separateTokenRequest) {
59
+ return normalizeError(error, { props: {
60
+ failedConnectionStep,
61
+ separateTokenRequest,
62
+ } });
63
+ }
64
+ /**
65
+ * Connects to a delta stream endpoint for emitting ops.
66
+ *
67
+ * @returns returns the document delta stream service for onedrive/sharepoint driver.
68
+ */
69
+ async connectToDeltaStream(client) {
70
+ assert(this.currentConnection === undefined, 0x4ad /* Should not be called when connection is already present! */);
71
+ // Attempt to connect twice, in case we used expired token.
72
+ return getWithRetryForTokenRefresh(async (options) => {
73
+ var _a;
74
+ // Presence of getWebsocketToken callback dictates whether callback is used for fetching
75
+ // websocket token or whether it is returned with joinSession response payload
76
+ const requestWebsocketTokenFromJoinSession = this.getWebsocketToken === undefined;
77
+ const websocketTokenPromise = requestWebsocketTokenFromJoinSession
78
+ ? Promise.resolve(null)
79
+ : this.getWebsocketToken(options);
80
+ const annotateAndRethrowConnectionError = (step) => (error) => {
81
+ throw this.annotateConnectionError(error, step, !requestWebsocketTokenFromJoinSession);
82
+ };
83
+ const joinSessionPromise = this.joinSession(requestWebsocketTokenFromJoinSession, options);
84
+ const [websocketEndpoint, websocketToken] = await Promise.all([
85
+ joinSessionPromise.catch(annotateAndRethrowConnectionError("joinSession")),
86
+ websocketTokenPromise.catch(annotateAndRethrowConnectionError("getWebsocketToken")),
87
+ ]);
88
+ const finalWebsocketToken = websocketToken !== null && websocketToken !== void 0 ? websocketToken : ((_a = websocketEndpoint.socketToken) !== null && _a !== void 0 ? _a : null);
89
+ if (finalWebsocketToken === null) {
90
+ throw this.annotateConnectionError(new NonRetryableError("Websocket token is null", OdspErrorType.fetchTokenError, { driverVersion }), "getWebsocketToken", !requestWebsocketTokenFromJoinSession);
91
+ }
92
+ try {
93
+ const connection = await this.createDeltaConnection(websocketEndpoint.tenantId, websocketEndpoint.id, finalWebsocketToken, client, websocketEndpoint.deltaStreamSocketUrl);
94
+ connection.on("op", (documentId, ops) => {
95
+ this.opsReceived(ops);
96
+ });
97
+ // On disconnect with 401/403 error code, we can just clear the joinSession cache as we will again
98
+ // get the auth error on reconnecting and face latency.
99
+ connection.once("disconnect", (error) => {
100
+ // Clear the join session refresh timer so that it can be restarted on reconnection.
101
+ this.clearJoinSessionTimer();
102
+ if (typeof error === "object" && error !== null
103
+ && error.errorType === DriverErrorType.authorizationError) {
104
+ this.cache.sessionJoinCache.remove(this.joinSessionKey);
105
+ }
106
+ // If we hit this assert, it means that "disconnect" event is emitted before the connection went through
107
+ // dispose flow which is not correct and could lead to a bunch of erros.
108
+ assert(connection.disposed, 0x4ae /* Connection should be disposed by now */);
109
+ this.currentConnection = undefined;
110
+ });
111
+ this.currentConnection = connection;
112
+ return connection;
113
+ }
114
+ catch (error) {
115
+ this.cache.sessionJoinCache.remove(this.joinSessionKey);
116
+ const normalizedError = this.annotateConnectionError(error, "createDeltaConnection", !requestWebsocketTokenFromJoinSession);
117
+ if (typeof error === "object" && error !== null) {
118
+ normalizedError.addTelemetryProperties({ socketDocumentId: websocketEndpoint.id });
119
+ }
120
+ throw normalizedError;
121
+ }
122
+ });
123
+ }
124
+ clearJoinSessionTimer() {
125
+ if (this.joinSessionRefreshTimer !== undefined) {
126
+ clearTimeout(this.joinSessionRefreshTimer);
127
+ this.joinSessionRefreshTimer = undefined;
128
+ }
129
+ }
130
+ async scheduleJoinSessionRefresh(delta) {
131
+ await new Promise((resolve, reject) => {
132
+ this.joinSessionRefreshTimer = setTimeout(() => {
133
+ getWithRetryForTokenRefresh(async (options) => {
134
+ await this.joinSession(false, options);
135
+ resolve();
136
+ }).catch((error) => {
137
+ reject(error);
138
+ });
139
+ }, delta);
140
+ });
141
+ }
142
+ async joinSession(requestSocketToken, options) {
143
+ const response = await this.joinSessionCore(requestSocketToken, options).catch((e) => {
144
+ if (hasFacetCodes(e) && e.facetCodes !== undefined) {
145
+ for (const code of e.facetCodes) {
146
+ switch (code) {
147
+ case "sessionForbiddenOnPreservedFiles":
148
+ case "sessionForbiddenOnModerationEnabledLibrary":
149
+ case "sessionForbiddenOnRequireCheckout":
150
+ // This document can only be opened in storage-only mode.
151
+ // DeltaManager will recognize this error
152
+ // and load without a delta stream connection.
153
+ this.policies = Object.assign(Object.assign({}, this.policies), { storageOnly: true });
154
+ throw new DeltaStreamConnectionForbiddenError(code, { driverVersion });
155
+ default:
156
+ continue;
157
+ }
158
+ }
159
+ }
160
+ throw e;
161
+ });
162
+ this._relayServiceTenantAndSessionId = `${response.tenantId}/${response.id}`;
163
+ return response;
164
+ }
165
+ async joinSessionCore(requestSocketToken, options) {
166
+ const disableJoinSessionRefresh = this.mc.config.getBoolean("Fluid.Driver.Odsp.disableJoinSessionRefresh");
167
+ const executeFetch = async () => {
168
+ var _a;
169
+ const joinSessionResponse = await fetchJoinSession(this.odspResolvedUrl, "opStream/joinSession", "POST", this.mc.logger, this.getStorageToken, this.epochTracker, requestSocketToken, options, disableJoinSessionRefresh, (_a = this.hostPolicy.sessionOptions) === null || _a === void 0 ? void 0 : _a.unauthenticatedUserDisplayName);
170
+ return {
171
+ entryTime: Date.now(),
172
+ joinSessionResponse,
173
+ };
174
+ };
175
+ const getResponseAndRefreshAfterDeltaMs = async () => {
176
+ var _a;
177
+ const _response = await this.cache.sessionJoinCache.addOrGet(this.joinSessionKey, executeFetch);
178
+ // If the response does not contain refreshSessionDurationSeconds, then treat it as old flow and let the
179
+ // cache entry to be treated as expired after 1 hour.
180
+ _response.joinSessionResponse.refreshSessionDurationSeconds =
181
+ (_a = _response.joinSessionResponse.refreshSessionDurationSeconds) !== null && _a !== void 0 ? _a : 3600;
182
+ return Object.assign(Object.assign({}, _response), { refreshAfterDeltaMs: this.calculateJoinSessionRefreshDelta(_response.entryTime, _response.joinSessionResponse.refreshSessionDurationSeconds) });
183
+ };
184
+ let response = await getResponseAndRefreshAfterDeltaMs();
185
+ // This means that the cached entry has expired(This should not be possible if the response is fetched
186
+ // from the network call). In this case we remove the cached entry and fetch the new response.
187
+ if (response.refreshAfterDeltaMs <= 0) {
188
+ this.cache.sessionJoinCache.remove(this.joinSessionKey);
189
+ response = await getResponseAndRefreshAfterDeltaMs();
190
+ }
191
+ if (!disableJoinSessionRefresh) {
192
+ const props = {
193
+ entryTime: response.entryTime,
194
+ refreshSessionDurationSeconds: response.joinSessionResponse.refreshSessionDurationSeconds,
195
+ refreshAfterDeltaMs: response.refreshAfterDeltaMs,
196
+ };
197
+ if (response.refreshAfterDeltaMs > 0) {
198
+ this.scheduleJoinSessionRefresh(response.refreshAfterDeltaMs)
199
+ .catch((error) => {
200
+ const canRetry = canRetryOnError(error);
201
+ // Only record error event in case it is non retriable.
202
+ if (!canRetry) {
203
+ this.mc.logger.sendErrorEvent({
204
+ eventName: "JoinSessionRefreshError",
205
+ details: JSON.stringify(props),
206
+ }, error);
207
+ }
208
+ });
209
+ }
210
+ else {
211
+ // Logging just for informational purposes to help with debugging as this is a new feature.
212
+ this.mc.logger.sendTelemetryEvent({
213
+ eventName: "JoinSessionRefreshNotScheduled",
214
+ details: JSON.stringify(props),
215
+ });
216
+ }
217
+ }
218
+ return response.joinSessionResponse;
219
+ }
220
+ calculateJoinSessionRefreshDelta(responseFetchTime, refreshSessionDurationSeconds) {
221
+ // 30 seconds is buffer time to refresh the session.
222
+ return responseFetchTime + ((refreshSessionDurationSeconds * 1000) - 30000) - Date.now();
223
+ }
224
+ /**
225
+ * Creats a connection to the given delta stream endpoint
226
+ *
227
+ * @param tenantId - the ID of the tenant
228
+ * @param documentId - document ID
229
+ * @param token - authorization token for delta service
230
+ * @param client - information about the client
231
+ * @param webSocketUrl - websocket URL
232
+ */
233
+ async createDeltaConnection(tenantId, documentId, token, client, webSocketUrl) {
234
+ const startTime = performance.now();
235
+ const connection = await OdspDocumentDeltaConnection.create(tenantId, documentId, token, client, webSocketUrl, this.mc.logger, 60000, this.epochTracker, this.socketReferenceKeyPrefix);
236
+ const duration = performance.now() - startTime;
237
+ // This event happens rather often, so it adds up to cost of telemetry.
238
+ // Given that most reconnects result in reusing socket and happen very quickly,
239
+ // report event only if it took longer than threshold.
240
+ if (duration >= 2000) {
241
+ this.mc.logger.sendPerformanceEvent({
242
+ eventName: "ConnectionSuccess",
243
+ duration,
244
+ });
245
+ }
246
+ return connection;
247
+ }
248
+ dispose(error) {
249
+ var _a;
250
+ this.clearJoinSessionTimer();
251
+ (_a = this.currentConnection) === null || _a === void 0 ? void 0 : _a.dispose();
252
+ this.currentConnection = undefined;
253
+ }
254
+ }
255
+ //# sourceMappingURL=odspDelayLoadedDeltaStream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"odspDelayLoadedDeltaStream.js","sourceRoot":"","sources":["../src/odspDelayLoadedDeltaStream.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, performance } from \"@fluidframework/common-utils\";\nimport {\n IFluidErrorBase,\n MonitoringContext,\n normalizeError,\n} from \"@fluidframework/telemetry-utils\";\nimport {\n IDocumentDeltaConnection,\n IResolvedUrl,\n IDocumentServicePolicies,\n DriverErrorType,\n} from \"@fluidframework/driver-definitions\";\nimport { canRetryOnError, DeltaStreamConnectionForbiddenError, NonRetryableError } from \"@fluidframework/driver-utils\";\nimport {\n IClient,\n ISequencedDocumentMessage,\n} from \"@fluidframework/protocol-definitions\";\nimport {\n IOdspResolvedUrl,\n TokenFetchOptions,\n HostStoragePolicy,\n InstrumentedStorageTokenFetcher,\n OdspErrorType,\n} from \"@fluidframework/odsp-driver-definitions\";\nimport { hasFacetCodes } from \"@fluidframework/odsp-doclib-utils\";\nimport { ISocketStorageDiscovery } from \"./contracts\";\nimport { IOdspCache } from \"./odspCache\";\nimport { OdspDocumentDeltaConnection } from \"./odspDocumentDeltaConnection\";\nimport { getWithRetryForTokenRefresh, TokenFetchOptionsEx } from \"./odspUtils\";\nimport { fetchJoinSession } from \"./vroom\";\nimport { EpochTracker } from \"./epochTracker\";\nimport { pkgVersion as driverVersion } from \"./packageVersion\";\n\n/**\n * This OdspDelayLoadedDeltaStream is used by OdspDocumentService.ts to delay load the delta connection\n * as they are not on critical path of loading a container.\n */\nexport class OdspDelayLoadedDeltaStream {\n // Timer which runs and executes the join session call after intervals.\n private joinSessionRefreshTimer: ReturnType<typeof setTimeout> | undefined;\n\n private readonly joinSessionKey: string;\n\n private currentConnection?: OdspDocumentDeltaConnection;\n\n private _relayServiceTenantAndSessionId: string | undefined;\n\n /**\n * @param odspResolvedUrl - resolved url identifying document that will be managed by this service instance.\n * @param policies - Document service policies.\n * @param getStorageToken - function that can provide the storage token. This is is also referred to as\n * the \"Vroom\" token in SPO.\n * @param getWebsocketToken - function that can provide a token for accessing the web socket. This is also referred\n * to as the \"Push\" token in SPO. If undefined then websocket token is expected to be returned with joinSession\n * response payload.\n * @param mc - a logger that can capture performance and diagnostic information\n * @param cache - This caches response for joinSession.\n * @param hostPolicy - host constructed policy which customizes service behavior.\n * @param epochTracker - This helper class which adds epoch to backend calls made by this service instance.\n * @param opsReceived - To register the ops received through socket.\n * @param socketReferenceKeyPrefix - (optional) prefix to isolate socket reuse cache\n */\n public constructor(\n public readonly odspResolvedUrl: IOdspResolvedUrl,\n public policies: IDocumentServicePolicies,\n private readonly getStorageToken: InstrumentedStorageTokenFetcher,\n private readonly getWebsocketToken: ((options: TokenFetchOptions) => Promise<string | null>) | undefined,\n private readonly mc: MonitoringContext,\n private readonly cache: IOdspCache,\n private readonly hostPolicy: HostStoragePolicy,\n private readonly epochTracker: EpochTracker,\n private readonly opsReceived: (ops: ISequencedDocumentMessage[]) => void, \n private readonly socketReferenceKeyPrefix?: string,\n ) {\n this.joinSessionKey = `${this.odspResolvedUrl.hashedDocumentId}/joinsession`;\n }\n\n public get resolvedUrl(): IResolvedUrl {\n return this.odspResolvedUrl;\n }\n\n public get currentDeltaConnection(): OdspDocumentDeltaConnection | undefined {\n return this.currentConnection;\n }\n\n public get relayServiceTenantAndSessionId(): string | undefined {\n return this._relayServiceTenantAndSessionId;\n }\n\n /** Annotate the given error indicating which connection step failed */\n private annotateConnectionError(\n error: any,\n failedConnectionStep: string,\n separateTokenRequest: boolean,\n ): IFluidErrorBase {\n return normalizeError(error, { props: {\n failedConnectionStep,\n separateTokenRequest,\n } });\n }\n\n /**\n * Connects to a delta stream endpoint for emitting ops.\n *\n * @returns returns the document delta stream service for onedrive/sharepoint driver.\n */\n public async connectToDeltaStream(client: IClient): Promise<IDocumentDeltaConnection> {\n assert(this.currentConnection === undefined, 0x4ad /* Should not be called when connection is already present! */);\n // Attempt to connect twice, in case we used expired token.\n return getWithRetryForTokenRefresh<IDocumentDeltaConnection>(async (options) => {\n // Presence of getWebsocketToken callback dictates whether callback is used for fetching\n // websocket token or whether it is returned with joinSession response payload\n const requestWebsocketTokenFromJoinSession = this.getWebsocketToken === undefined;\n const websocketTokenPromise = requestWebsocketTokenFromJoinSession\n ? Promise.resolve(null)\n : this.getWebsocketToken!(options);\n\n const annotateAndRethrowConnectionError = (step: string) => (error: any) => {\n throw this.annotateConnectionError(error, step, !requestWebsocketTokenFromJoinSession);\n };\n\n const joinSessionPromise = this.joinSession(requestWebsocketTokenFromJoinSession, options);\n const [websocketEndpoint, websocketToken] =\n await Promise.all([\n joinSessionPromise.catch(annotateAndRethrowConnectionError(\"joinSession\")),\n websocketTokenPromise.catch(annotateAndRethrowConnectionError(\"getWebsocketToken\")),\n ]);\n\n const finalWebsocketToken = websocketToken ?? (websocketEndpoint.socketToken ?? null);\n if (finalWebsocketToken === null) {\n throw this.annotateConnectionError(\n new NonRetryableError(\n \"Websocket token is null\",\n OdspErrorType.fetchTokenError,\n { driverVersion },\n ),\n \"getWebsocketToken\",\n !requestWebsocketTokenFromJoinSession);\n }\n try {\n const connection = await this.createDeltaConnection(\n websocketEndpoint.tenantId,\n websocketEndpoint.id,\n finalWebsocketToken,\n client,\n websocketEndpoint.deltaStreamSocketUrl);\n connection.on(\"op\", (documentId, ops: ISequencedDocumentMessage[]) => {\n this.opsReceived(ops);\n });\n // On disconnect with 401/403 error code, we can just clear the joinSession cache as we will again\n // get the auth error on reconnecting and face latency.\n connection.once(\"disconnect\", (error: any) => {\n // Clear the join session refresh timer so that it can be restarted on reconnection.\n this.clearJoinSessionTimer();\n if (typeof error === \"object\" && error !== null\n && error.errorType === DriverErrorType.authorizationError) {\n this.cache.sessionJoinCache.remove(this.joinSessionKey);\n }\n // If we hit this assert, it means that \"disconnect\" event is emitted before the connection went through\n // dispose flow which is not correct and could lead to a bunch of erros.\n assert(connection.disposed, 0x4ae /* Connection should be disposed by now */);\n this.currentConnection = undefined;\n });\n this.currentConnection = connection;\n return connection;\n } catch (error) {\n this.cache.sessionJoinCache.remove(this.joinSessionKey);\n\n const normalizedError = this.annotateConnectionError(\n error,\n \"createDeltaConnection\",\n !requestWebsocketTokenFromJoinSession);\n if (typeof error === \"object\" && error !== null) {\n normalizedError.addTelemetryProperties({ socketDocumentId: websocketEndpoint.id });\n }\n throw normalizedError;\n }\n });\n }\n\n private clearJoinSessionTimer() {\n if (this.joinSessionRefreshTimer !== undefined) {\n clearTimeout(this.joinSessionRefreshTimer);\n this.joinSessionRefreshTimer = undefined;\n }\n }\n\n private async scheduleJoinSessionRefresh(delta: number) {\n await new Promise<void>((resolve, reject) => {\n this.joinSessionRefreshTimer = setTimeout(() => {\n getWithRetryForTokenRefresh(async (options) => {\n await this.joinSession(false, options);\n resolve();\n }).catch((error) => {\n reject(error);\n });\n }, delta);\n });\n }\n\n private async joinSession(\n requestSocketToken: boolean,\n options: TokenFetchOptionsEx,\n ) {\n const response = await this.joinSessionCore(requestSocketToken, options).catch((e) => {\n if (hasFacetCodes(e) && e.facetCodes !== undefined) {\n for (const code of e.facetCodes) {\n switch (code) {\n case \"sessionForbiddenOnPreservedFiles\":\n case \"sessionForbiddenOnModerationEnabledLibrary\":\n case \"sessionForbiddenOnRequireCheckout\":\n // This document can only be opened in storage-only mode.\n // DeltaManager will recognize this error\n // and load without a delta stream connection.\n this.policies = { ...this.policies, storageOnly: true };\n throw new DeltaStreamConnectionForbiddenError(code, { driverVersion });\n default:\n continue;\n }\n }\n }\n throw e;\n });\n this._relayServiceTenantAndSessionId = `${response.tenantId}/${response.id}`;\n return response;\n }\n\n private async joinSessionCore(\n requestSocketToken: boolean,\n options: TokenFetchOptionsEx,\n ): Promise<ISocketStorageDiscovery> {\n const disableJoinSessionRefresh = this.mc.config.getBoolean(\"Fluid.Driver.Odsp.disableJoinSessionRefresh\");\n const executeFetch = async () => {\n const joinSessionResponse = await fetchJoinSession(\n this.odspResolvedUrl,\n \"opStream/joinSession\",\n \"POST\",\n this.mc.logger,\n this.getStorageToken,\n this.epochTracker,\n requestSocketToken,\n options,\n disableJoinSessionRefresh,\n this.hostPolicy.sessionOptions?.unauthenticatedUserDisplayName,\n );\n return {\n entryTime: Date.now(),\n joinSessionResponse,\n };\n };\n\n const getResponseAndRefreshAfterDeltaMs = async () => {\n const _response = await this.cache.sessionJoinCache.addOrGet(this.joinSessionKey, executeFetch);\n // If the response does not contain refreshSessionDurationSeconds, then treat it as old flow and let the\n // cache entry to be treated as expired after 1 hour.\n _response.joinSessionResponse.refreshSessionDurationSeconds =\n _response.joinSessionResponse.refreshSessionDurationSeconds ?? 3600;\n return {\n ..._response,\n refreshAfterDeltaMs: this.calculateJoinSessionRefreshDelta(\n _response.entryTime, _response.joinSessionResponse.refreshSessionDurationSeconds),\n };\n };\n let response = await getResponseAndRefreshAfterDeltaMs();\n // This means that the cached entry has expired(This should not be possible if the response is fetched\n // from the network call). In this case we remove the cached entry and fetch the new response.\n if (response.refreshAfterDeltaMs <= 0) {\n this.cache.sessionJoinCache.remove(this.joinSessionKey);\n response = await getResponseAndRefreshAfterDeltaMs();\n }\n if (!disableJoinSessionRefresh) {\n const props = {\n entryTime: response.entryTime,\n refreshSessionDurationSeconds:\n response.joinSessionResponse.refreshSessionDurationSeconds,\n refreshAfterDeltaMs: response.refreshAfterDeltaMs,\n };\n if (response.refreshAfterDeltaMs > 0) {\n this.scheduleJoinSessionRefresh(response.refreshAfterDeltaMs)\n .catch((error) => {\n const canRetry = canRetryOnError(error);\n // Only record error event in case it is non retriable.\n if (!canRetry) {\n this.mc.logger.sendErrorEvent({\n eventName: \"JoinSessionRefreshError\",\n details: JSON.stringify(props),\n },\n error,\n );\n }\n });\n } else {\n // Logging just for informational purposes to help with debugging as this is a new feature.\n this.mc.logger.sendTelemetryEvent({\n eventName: \"JoinSessionRefreshNotScheduled\",\n details: JSON.stringify(props),\n });\n }\n }\n return response.joinSessionResponse;\n }\n\n private calculateJoinSessionRefreshDelta(responseFetchTime: number, refreshSessionDurationSeconds: number) {\n // 30 seconds is buffer time to refresh the session.\n return responseFetchTime + ((refreshSessionDurationSeconds * 1000) - 30000) - Date.now();\n }\n\n /**\n * Creats a connection to the given delta stream endpoint\n *\n * @param tenantId - the ID of the tenant\n * @param documentId - document ID\n * @param token - authorization token for delta service\n * @param client - information about the client\n * @param webSocketUrl - websocket URL\n */\n private async createDeltaConnection(\n tenantId: string,\n documentId: string,\n token: string | null,\n client: IClient,\n webSocketUrl: string,\n ): Promise<OdspDocumentDeltaConnection> {\n const startTime = performance.now();\n const connection = await OdspDocumentDeltaConnection.create(\n tenantId,\n documentId,\n token,\n client,\n webSocketUrl,\n this.mc.logger,\n 60000,\n this.epochTracker,\n this.socketReferenceKeyPrefix,\n );\n const duration = performance.now() - startTime;\n // This event happens rather often, so it adds up to cost of telemetry.\n // Given that most reconnects result in reusing socket and happen very quickly,\n // report event only if it took longer than threshold.\n if (duration >= 2000) {\n this.mc.logger.sendPerformanceEvent({\n eventName: \"ConnectionSuccess\",\n duration,\n });\n }\n return connection;\n }\n\n public dispose(error?: any) {\n this.clearJoinSessionTimer();\n this.currentConnection?.dispose();\n this.currentConnection = undefined;\n }\n}\n"]}
@@ -8,7 +8,6 @@ import { IAnyDriverError } from "@fluidframework/driver-definitions";
8
8
  import { OdspError } from "@fluidframework/odsp-driver-definitions";
9
9
  import { IFluidErrorBase } from "@fluidframework/telemetry-utils";
10
10
  import { IClient, IConnect } from "@fluidframework/protocol-definitions";
11
- import type { io as SocketIOClientStatic } from "socket.io-client";
12
11
  import { EpochTracker } from "./epochTracker";
13
12
  export interface FlushResult {
14
13
  lastPersistedSequenceNumber?: number;
@@ -26,7 +25,6 @@ export declare class OdspDocumentDeltaConnection extends DocumentDeltaConnection
26
25
  * @param tenantId - the ID of the tenant
27
26
  * @param documentId - document ID
28
27
  * @param token - authorization token for storage service
29
- * @param io - websocket library
30
28
  * @param client - information about the client
31
29
  * @param mode - mode of the client
32
30
  * @param url - websocket URL
@@ -35,7 +33,7 @@ export declare class OdspDocumentDeltaConnection extends DocumentDeltaConnection
35
33
  * @param epochTracker - track epoch changes
36
34
  * @param socketReferenceKeyPrefix - (optional) prefix to isolate socket reuse cache
37
35
  */
38
- static create(tenantId: string, documentId: string, token: string | null, io: typeof SocketIOClientStatic, client: IClient, url: string, telemetryLogger: ITelemetryLogger, timeoutMs: number, epochTracker: EpochTracker, socketReferenceKeyPrefix: string | undefined): Promise<OdspDocumentDeltaConnection>;
36
+ static create(tenantId: string, documentId: string, token: string | null, client: IClient, url: string, telemetryLogger: ITelemetryLogger, timeoutMs: number, epochTracker: EpochTracker, socketReferenceKeyPrefix: string | undefined): Promise<OdspDocumentDeltaConnection>;
39
37
  private socketReference;
40
38
  private readonly requestOpsNoncePrefix;
41
39
  private pushCallCounter;
@@ -1 +1 @@
1
- {"version":3,"file":"odspDocumentDeltaConnection.d.ts","sourceRoot":"","sources":["../src/odspDocumentDeltaConnection.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAU,MAAM,oCAAoC,CAAC;AAE9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AACrE,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,eAAe,EAA6B,MAAM,iCAAiC,CAAC;AAC7F,OAAO,EACH,OAAO,EACP,QAAQ,EAIX,MAAM,sCAAsC,CAAC;AAC9C,OAAO,KAAK,EAAU,EAAE,IAAI,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAG3E,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAQ9C,MAAM,WAAW,WAAW;IACxB,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AA2JD;;GAEG;AACH,qBAAa,2BAA4B,SAAQ,uBAAuB;IAmKhE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC;IAlKxC;;;;;;;;;;;;;;;OAeG;WACiB,MAAM,CACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,EAAE,EAAE,OAAO,oBAAoB,EAC/B,MAAM,EAAE,OAAO,EACf,GAAG,EAAE,MAAM,EACX,eAAe,EAAE,gBAAgB,EACjC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,YAAY,EAC1B,wBAAwB,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,2BAA2B,CAAC;IAuEvF,OAAO,CAAC,eAAe,CAA8B;IAErD,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAS;IAC/C,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwE;IAClG,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,aAAa,CAAoC;IAEzD;;OAEG;IACH,SAAS,CAAC,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,EAAE,QAAQ,UAAO,GAAG,eAAe;IAS3F;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,4BAA4B;IA8B3C;;;;;;OAMG;IACH,OAAO;IAYP;;;;;OAKG;IACK,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM;IA0D9B,KAAK,IAAI,OAAO,CAAC,WAAW,CAAC;IA8B1C,SAAS,CAAC,iBAAiB,UAAW,eAAe,GAAG,SAAS,yCAK/D;cAEc,UAAU,CAAC,cAAc,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM;IAuFpE,SAAS,CAAC,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI;IAiD9E;;;OAGG;IACH,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,eAAe;IAO5C;;OAEG;IACH,SAAS,CAAC,cAAc;CAa3B"}
1
+ {"version":3,"file":"odspDocumentDeltaConnection.d.ts","sourceRoot":"","sources":["../src/odspDocumentDeltaConnection.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAU,MAAM,oCAAoC,CAAC;AAE9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AACrE,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,eAAe,EAA6B,MAAM,iCAAiC,CAAC;AAC7F,OAAO,EACH,OAAO,EACP,QAAQ,EAIX,MAAM,sCAAsC,CAAC;AAK9C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAQ9C,MAAM,WAAW,WAAW;IACxB,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAgKD;;GAEG;AACH,qBAAa,2BAA4B,SAAQ,uBAAuB;IAgKhE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC;IA/JxC;;;;;;;;;;;;;;OAcG;WACiB,MAAM,CACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,MAAM,EAAE,OAAO,EACf,GAAG,EAAE,MAAM,EACX,eAAe,EAAE,gBAAgB,EACjC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,YAAY,EAC1B,wBAAwB,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,2BAA2B,CAAC;IAuEvF,OAAO,CAAC,eAAe,CAA8B;IAErD,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAS;IAC/C,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwE;IAClG,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,aAAa,CAAoC;IAEzD;;OAEG;IACH,SAAS,CAAC,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,EAAE,QAAQ,UAAO,GAAG,eAAe;IAS3F;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,4BAA4B;IA6B3C;;;;;;OAMG;IACH,OAAO;IAYP;;;;;OAKG;IACK,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM;IA0D9B,KAAK,IAAI,OAAO,CAAC,WAAW,CAAC;IA8B1C,SAAS,CAAC,iBAAiB,UAAW,eAAe,GAAG,SAAS,yCAK/D;cAEc,UAAU,CAAC,cAAc,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM;IAuFpE,SAAS,CAAC,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI;IAiD9E;;;OAGG;IACH,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,eAAe;IAO5C;;OAEG;IACH,SAAS,CAAC,cAAc;CAa3B"}
@@ -5,7 +5,9 @@
5
5
  import { assert, performance, Deferred, TypedEventEmitter } from "@fluidframework/common-utils";
6
6
  import { DocumentDeltaConnection } from "@fluidframework/driver-base";
7
7
  import { loggerToMonitoringContext } from "@fluidframework/telemetry-utils";
8
+ import { io as SocketIOClientStatic } from "socket.io-client";
8
9
  import { v4 as uuid } from "uuid";
10
+ import { createGenericNetworkError } from "@fluidframework/driver-utils";
9
11
  import { errorObjectFromSocketError } from "./odspError";
10
12
  import { pkgVersion } from "./packageVersion";
11
13
  const protocolVersions = ["^0.4.0", "^0.3.0", "^0.2.0", "^0.1.0"];
@@ -110,8 +112,8 @@ class SocketReference extends TypedEventEmitter {
110
112
  // Disconnect flow should be synchronous and result in system fully forgetting about this connection / socket.
111
113
  const socket = this._socket;
112
114
  this._socket = undefined;
113
- // Let all connections know they need to go through disconnect flow
114
- this.emit("disconnect", error, undefined /* clientId */);
115
+ // Let all connections know they need to go through disconnect flow.
116
+ this.emit("disconnect", error !== null && error !== void 0 ? error : createGenericNetworkError("Socket closed without error", { canRetry: true }, { driverVersion: pkgVersion }), undefined /* clientId */);
115
117
  // We should not have any users now, assuming synchronous disconnect flow in response to
116
118
  // "disconnect" event
117
119
  assert(this.references === 0, 0x412 /* Nobody should be connected to this socket at this point! */);
@@ -168,7 +170,6 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection {
168
170
  * @param tenantId - the ID of the tenant
169
171
  * @param documentId - document ID
170
172
  * @param token - authorization token for storage service
171
- * @param io - websocket library
172
173
  * @param client - information about the client
173
174
  * @param mode - mode of the client
174
175
  * @param url - websocket URL
@@ -177,7 +178,7 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection {
177
178
  * @param epochTracker - track epoch changes
178
179
  * @param socketReferenceKeyPrefix - (optional) prefix to isolate socket reuse cache
179
180
  */
180
- static async create(tenantId, documentId, token, io, client, url, telemetryLogger, timeoutMs, epochTracker, socketReferenceKeyPrefix) {
181
+ static async create(tenantId, documentId, token, client, url, telemetryLogger, timeoutMs, epochTracker, socketReferenceKeyPrefix) {
181
182
  const mc = loggerToMonitoringContext(telemetryLogger);
182
183
  // enable multiplexing when the websocket url does not include the tenant/document id
183
184
  const parsedUrl = new URL(url);
@@ -186,7 +187,7 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection {
186
187
  // this will allow multiple documents to share the same websocket connection
187
188
  const key = socketReferenceKeyPrefix ? `${socketReferenceKeyPrefix},${url}` : url;
188
189
  const socketReferenceKey = enableMultiplexing ? key : `${key},${tenantId},${documentId}`;
189
- const socketReference = OdspDocumentDeltaConnection.getOrCreateSocketIoReference(io, timeoutMs, socketReferenceKey, url, enableMultiplexing, tenantId, documentId, telemetryLogger);
190
+ const socketReference = OdspDocumentDeltaConnection.getOrCreateSocketIoReference(timeoutMs, socketReferenceKey, url, enableMultiplexing, tenantId, documentId, telemetryLogger);
190
191
  const socket = socketReference.socket;
191
192
  const connectMessage = {
192
193
  client,
@@ -247,13 +248,13 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection {
247
248
  /**
248
249
  * Gets or create a socket io connection for the given key
249
250
  */
250
- static getOrCreateSocketIoReference(io, timeoutMs, key, url, enableMultiplexing, tenantId, documentId, logger) {
251
+ static getOrCreateSocketIoReference(timeoutMs, key, url, enableMultiplexing, tenantId, documentId, logger) {
251
252
  const existingSocketReference = SocketReference.find(key, logger);
252
253
  if (existingSocketReference) {
253
254
  return existingSocketReference;
254
255
  }
255
256
  const query = enableMultiplexing ? undefined : { documentId, tenantId };
256
- const socket = io(url, {
257
+ const socket = SocketIOClientStatic(url, {
257
258
  multiplex: false,
258
259
  query,
259
260
  reconnection: false,
@@ -1 +1 @@
1
- {"version":3,"file":"odspDocumentDeltaConnection.js","sourceRoot":"","sources":["../src/odspDocumentDeltaConnection.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 { ITelemetryLogger, IEvent } from \"@fluidframework/common-definitions\";\nimport { assert, performance, Deferred, TypedEventEmitter } from \"@fluidframework/common-utils\";\nimport { DocumentDeltaConnection } from \"@fluidframework/driver-base\";\nimport { IAnyDriverError } from \"@fluidframework/driver-definitions\";\nimport { OdspError } from \"@fluidframework/odsp-driver-definitions\";\nimport { IFluidErrorBase, loggerToMonitoringContext } from \"@fluidframework/telemetry-utils\";\nimport {\n IClient,\n IConnect,\n INack,\n ISequencedDocumentMessage,\n ISignalMessage,\n} from \"@fluidframework/protocol-definitions\";\nimport type { Socket, io as SocketIOClientStatic } from \"socket.io-client\";\nimport { v4 as uuid } from \"uuid\";\nimport { IOdspSocketError, IGetOpsResponse, IFlushOpsResponse } from \"./contracts\";\nimport { EpochTracker } from \"./epochTracker\";\nimport { errorObjectFromSocketError } from \"./odspError\";\nimport { pkgVersion } from \"./packageVersion\";\n\nconst protocolVersions = [\"^0.4.0\", \"^0.3.0\", \"^0.2.0\", \"^0.1.0\"];\nconst feature_get_ops = \"api_get_ops\";\nconst feature_flush_ops = \"api_flush_ops\";\n\nexport interface FlushResult {\n lastPersistedSequenceNumber?: number;\n retryAfter?: number;\n}\n\n// How long to wait before disconnecting the socket after the last reference is removed\n// This allows reconnection after receiving a nack to be smooth\nconst socketReferenceBufferTime = 2000;\n\ninterface ISocketEvents extends IEvent {\n (event: \"disconnect\", listener: (error: IFluidErrorBase & OdspError, clientId?: string) => void);\n}\n\nclass SocketReference extends TypedEventEmitter<ISocketEvents> {\n private references: number = 1;\n private delayDeleteTimeout: ReturnType<typeof setTimeout> | undefined;\n private _socket: Socket | undefined;\n\n // When making decisions about socket reuse, we do not reuse disconnected socket.\n // But we want to differentiate the following case from disconnected case:\n // Socket that never connected and never failed, it's in \"attempting to connect\" mode\n // such sockets should be reused, despite socket.disconnected === true\n private isPendingInitialConnection = true;\n\n // Map of all existing socket io sockets. [url, tenantId, documentId] -> socket\n private static readonly socketIoSockets: Map<string, SocketReference> = new Map();\n\n public static find(key: string, logger: ITelemetryLogger) {\n const socketReference = SocketReference.socketIoSockets.get(key);\n\n // Verify the socket is healthy before reusing it\n if (socketReference?.disconnected) {\n // The socket is in a bad state. fully remove the reference\n socketReference.closeSocket();\n return undefined;\n }\n\n if (socketReference) {\n // Clear the pending deletion if there is one\n socketReference.clearTimer();\n socketReference.references++;\n }\n\n return socketReference;\n }\n\n /**\n * Removes a reference for the given key\n * Once the ref count hits 0, the socket is disconnected and removed\n */\n public removeSocketIoReference() {\n assert(this.references > 0, 0x09f /* \"No more socketIO refs to remove!\" */);\n this.references--;\n\n // see comment in disconnected() getter\n this.isPendingInitialConnection = false;\n\n if (this.disconnected) {\n this.closeSocket();\n return;\n }\n\n if (this.references === 0 && this.delayDeleteTimeout === undefined) {\n this.delayDeleteTimeout = setTimeout(() => {\n // We should not get here with active users.\n assert(this.references === 0, 0x0a0 /* \"Unexpected socketIO references on timeout\" */);\n this.closeSocket();\n }, socketReferenceBufferTime);\n }\n }\n\n public get socket() {\n if (!this._socket) {\n throw new Error(`Invalid socket for key \"${this.key}`);\n }\n return this._socket;\n }\n\n public constructor(public readonly key: string, socket: Socket) {\n super();\n\n this._socket = socket;\n assert(!SocketReference.socketIoSockets.has(key), 0x220 /* \"socket key collision\" */);\n SocketReference.socketIoSockets.set(key, this);\n\n // Server sends this event when it wants to disconnect a particular client in which case the client id would\n // be present or if it wants to disconnect all the clients. The server always closes the socket in case all\n // clients needs to be disconnected. So fully remove the socket reference in this case.\n socket.on(\"server_disconnect\", (socketError: IOdspSocketError, clientId?: string) => {\n // Treat all errors as recoverable, and rely on joinSession / reconnection flow to\n // filter out retryable vs. non-retryable cases.\n const error = errorObjectFromSocketError(socketError, \"server_disconnect\");\n error.addTelemetryProperties({ disconnectClientId: clientId });\n error.canRetry = true;\n\n // see comment in disconnected() getter\n // Setting it here to ensure socket reuse does not happen if new request to connect\n // comes in from \"disconnect\" listener below, before we close socket.\n this.isPendingInitialConnection = false;\n\n if (clientId === undefined) {\n // We could first raise \"disconnect\" event, but that may result in socket reuse due to\n // new connection comming in. So, it's better to have more explicit flow to make it impossible.\n this.closeSocket(error);\n } else {\n this.emit(\"disconnect\", error, clientId);\n }\n });\n }\n\n private clearTimer() {\n if (this.delayDeleteTimeout !== undefined) {\n clearTimeout(this.delayDeleteTimeout);\n this.delayDeleteTimeout = undefined;\n }\n }\n\n public closeSocket(error?: IAnyDriverError) {\n if (!this._socket) { return; }\n\n this.clearTimer();\n\n assert(SocketReference.socketIoSockets.get(this.key) === this,\n 0x0a1 /* \"Socket reference set unexpectedly does not point to this socket!\" */);\n\n // First, remove socket to ensure no socket reuse is possible.\n SocketReference.socketIoSockets.delete(this.key);\n\n // Block access to socket. From now on, calls like flush() or requestOps()\n // Disconnect flow should be synchronous and result in system fully forgetting about this connection / socket.\n const socket = this._socket;\n this._socket = undefined;\n\n // Let all connections know they need to go through disconnect flow\n this.emit(\"disconnect\", error, undefined /* clientId */);\n\n // We should not have any users now, assuming synchronous disconnect flow in response to\n // \"disconnect\" event\n assert(this.references === 0, 0x412 /* Nobody should be connected to this socket at this point! */);\n\n socket.disconnect();\n }\n\n public get disconnected() {\n if (this._socket === undefined) { return true; }\n if (this.socket.connected) { return false; }\n\n // We have a socket that is not connected. Possible cases:\n // 1) It was connected some time ago and lost connection. We do not want to reuse it.\n // 2) It failed to connect (was never connected).\n // 3) It was just created and never had a chance to connect - connection is in process.\n // We have to differentiate 1 from 2-3 (specifically 1 & 3) in order to be able to reuse socket in #3.\n // We will use the fact that socket had some activity. I.e. if socket disconnected, or client stopped using\n // socket, then removeSocketIoReference() will be called for it, and it will be the indiction that it's not #3.\n return !this.isPendingInitialConnection;\n }\n}\n\n/**\n * Represents a connection to a stream of delta updates\n */\nexport class OdspDocumentDeltaConnection extends DocumentDeltaConnection {\n /**\n * Create a OdspDocumentDeltaConnection\n * If url #1 fails to connect, will try url #2 if applicable.\n *\n * @param tenantId - the ID of the tenant\n * @param documentId - document ID\n * @param token - authorization token for storage service\n * @param io - websocket library\n * @param client - information about the client\n * @param mode - mode of the client\n * @param url - websocket URL\n * @param telemetryLogger - optional telemetry logger\n * @param timeoutMs - time limit on making the connection\n * @param epochTracker - track epoch changes\n * @param socketReferenceKeyPrefix - (optional) prefix to isolate socket reuse cache\n */\n public static async create(\n tenantId: string,\n documentId: string,\n token: string | null,\n io: typeof SocketIOClientStatic,\n client: IClient,\n url: string,\n telemetryLogger: ITelemetryLogger,\n timeoutMs: number,\n epochTracker: EpochTracker,\n socketReferenceKeyPrefix: string | undefined): Promise<OdspDocumentDeltaConnection> {\n const mc = loggerToMonitoringContext(telemetryLogger);\n\n // enable multiplexing when the websocket url does not include the tenant/document id\n const parsedUrl = new URL(url);\n const enableMultiplexing = !parsedUrl.searchParams.has(\"documentId\") && !parsedUrl.searchParams.has(\"tenantId\");\n\n // do not include the specific tenant/doc id in the ref key when multiplexing\n // this will allow multiple documents to share the same websocket connection\n const key = socketReferenceKeyPrefix ? `${socketReferenceKeyPrefix},${url}` : url;\n const socketReferenceKey = enableMultiplexing ? key : `${key},${tenantId},${documentId}`;\n\n const socketReference = OdspDocumentDeltaConnection.getOrCreateSocketIoReference(\n io, timeoutMs, socketReferenceKey, url, enableMultiplexing, tenantId, documentId, telemetryLogger);\n\n const socket = socketReference.socket;\n\n const connectMessage: IConnect = {\n client,\n id: documentId,\n mode: client.mode,\n tenantId,\n token, // Token is going to indicate tenant level information, etc...\n versions: protocolVersions,\n driverVersion: pkgVersion,\n nonce: uuid(),\n epoch: epochTracker.fluidEpoch,\n relayUserAgent: [client.details.environment, ` driverVersion:${pkgVersion}`].join(\";\"),\n };\n\n // Reference to this client supporting get_ops flow.\n connectMessage.supportedFeatures = { };\n if (mc.config.getBoolean(\"Fluid.Driver.Odsp.GetOpsEnabled\") !== false) {\n connectMessage.supportedFeatures[feature_get_ops] = true;\n }\n\n const deltaConnection = new OdspDocumentDeltaConnection(\n socket,\n documentId,\n socketReference,\n telemetryLogger,\n enableMultiplexing);\n\n try {\n await deltaConnection.initialize(connectMessage, timeoutMs);\n await epochTracker.validateEpochFromPush(deltaConnection.details);\n } catch (errorObject: any) {\n if (errorObject !== null && typeof errorObject === \"object\") {\n // We have to special-case error types here in terms of what is re-triable.\n // These errors have to re-retried, we just need new joinSession result to connect to right server:\n // 400: Invalid tenant or document id. The WebSocket is connected to a different document\n // Document is full (with retryAfter)\n // 404: Invalid document. The document \\\"local/w1-...\\\" does not exist\n // But this has to stay not-retriable:\n // 406: Unsupported client protocol. This path is the only gatekeeper, have to fail!\n // 409: Epoch Version Mismatch. Client epoch and server epoch does not match, so app needs\n // to be refreshed.\n // This one is fine either way\n // 401/403: Code will retry once with new token either way, then it becomes fatal - on this path\n // and on join Session path.\n // 501: (Fluid not enabled): this is fine either way, as joinSession is gatekeeper\n if (errorObject.statusCode === 400 || errorObject.statusCode === 404) {\n errorObject.canRetry = true;\n }\n }\n throw errorObject;\n }\n\n return deltaConnection;\n }\n\n private socketReference: SocketReference | undefined;\n\n private readonly requestOpsNoncePrefix: string;\n private pushCallCounter = 0;\n private readonly getOpsMap: Map<string, { start: number; from: number; to: number; }> = new Map();\n private flushOpNonce: string | undefined;\n private flushDeferred: Deferred<FlushResult> | undefined;\n\n /**\n * Error raising for socket.io issues\n */\n protected createErrorObject(handler: string, error?: any, canRetry = true): IAnyDriverError {\n // Note: we suspect the incoming error object is either:\n // - a socketError: add it to the OdspError object for driver to be able to parse it and reason over it.\n // - anything else: let base class handle it\n return canRetry && Number.isInteger(error?.code) && typeof error?.message === \"string\"\n ? errorObjectFromSocketError(error as IOdspSocketError, handler)\n : super.createErrorObject(handler, error, canRetry);\n }\n\n /**\n * Gets or create a socket io connection for the given key\n */\n private static getOrCreateSocketIoReference(\n io: typeof SocketIOClientStatic,\n timeoutMs: number,\n key: string,\n url: string,\n enableMultiplexing: boolean,\n tenantId: string,\n documentId: string,\n logger: ITelemetryLogger,\n ): SocketReference {\n const existingSocketReference = SocketReference.find(key, logger);\n if (existingSocketReference) {\n return existingSocketReference;\n }\n\n const query = enableMultiplexing ? undefined : { documentId, tenantId };\n\n const socket = io(\n url,\n {\n multiplex: false, // Don't rely on socket.io built-in multiplexing\n query,\n reconnection: false,\n transports: [\"websocket\"],\n timeout: timeoutMs,\n });\n\n return new SocketReference(key, socket);\n }\n\n /**\n * @param socket - websocket to be used\n * @param documentId - ID of the document\n * @param details - details of the websocket connection\n * @param socketReferenceKey - socket reference key\n * @param enableMultiplexing - If the websocket is multiplexing multiple documents\n */\n private constructor(\n socket: Socket,\n documentId: string,\n socketReference: SocketReference,\n logger: ITelemetryLogger,\n private readonly enableMultiplexing?: boolean,\n ) {\n super(socket, documentId, logger);\n this.socketReference = socketReference;\n this.requestOpsNoncePrefix = `${uuid()}-`;\n }\n\n /**\n * Retrieves ops from PUSH\n * @param from - inclusive\n * @param to - exclusive\n * @returns ops retrieved\n */\n public requestOps(from: number, to: number) {\n assert(!this.socketReference?.disconnected, 0x413 /* non-active socket */);\n\n // Given that to is exclusive, we should be asking for at least something!\n assert(to > from, 0x272 /* \"empty request\" */);\n\n // PUSH may disable this functionality\n // back-compat: remove cast to any once latest version of IConnected is consumed\n if ((this.details as any).supportedFeatures?.[feature_get_ops] !== true) {\n return;\n }\n\n this.pushCallCounter++;\n const nonce = `${this.requestOpsNoncePrefix}${this.pushCallCounter}`;\n const start = performance.now();\n\n // We may keep keep accumulating memory for nothing, if we are not getting responses.\n // Note that we should not have overlapping requests, as DeltaManager allows only one\n // outstanding request to storage, and that's the only way to get here.\n // But requests could be cancelled, and thus overlapping requests might be in the picture\n // If it happens, we do not care about stale requests.\n // So track some number of requests, but log if we get too many in flight - that likely\n // indicates an error somewhere.\n if (this.getOpsMap.size >= 5) {\n let time = start;\n let key: string | undefined;\n for (const [keyCandidate, value] of this.getOpsMap.entries()) {\n if (value.start <= time || key === undefined) {\n time = value.start;\n key = keyCandidate;\n }\n }\n const payloadToDelete = this.getOpsMap.get(key!)!;\n this.logger.sendErrorEvent({\n eventName: \"GetOpsTooMany\",\n nonce,\n from: payloadToDelete.from,\n to: payloadToDelete.to,\n length: payloadToDelete.to - payloadToDelete.from,\n duration: performance.now() - payloadToDelete.start,\n });\n this.getOpsMap.delete(key!);\n }\n this.getOpsMap.set(\n nonce,\n {\n start,\n from,\n to,\n },\n );\n this.socket.emit(\"get_ops\", this.clientId, {\n nonce,\n from,\n to: to - 1,\n });\n }\n\n public async flush(): Promise<FlushResult> {\n assert(!this.socketReference?.disconnected, 0x414 /* non-active socket */);\n\n // back-compat: remove cast to any once latest version of IConnected is consumed\n if ((this.details as any).supportedFeatures?.[feature_flush_ops] !== true) {\n // Once single-commit summary is enabled end-to-end, flush support is a must!\n // The only alternative is change in design where SPO fetches ops from PUSH OR\n // summary includes required ops and SPO has some validation mechanism to ensure\n // they are not forged by client.\n // If design changes, we can reconsider it, but right now it's non-recoverable failure.\n this.logger.sendErrorEvent({ eventName: \"FlushOpsNotSupported\" });\n throw new Error(\"flush() API is not supported by PUSH, required for single-commit summaries\");\n }\n\n this.pushCallCounter++;\n const nonce = `${this.requestOpsNoncePrefix}${this.pushCallCounter}`;\n // There should be only one flush ops in flight, kicked out by upload summary workflow\n // That said, it could timeout and request could be repeated, so theoretically we can\n // get overlapping requests, but it should be very rare\n if (this.flushDeferred !== undefined) {\n this.logger.sendErrorEvent({ eventName: \"FlushOpsTooMany\" });\n this.flushDeferred.reject(\"process involving flush() was cancelled OR unsupported concurrency\");\n }\n this.socket.emit(\"flush_ops\", this.clientId, { nonce });\n\n this.flushOpNonce = nonce;\n this.flushDeferred = new Deferred<FlushResult>();\n return this.flushDeferred.promise;\n }\n\n protected disconnectHandler = (error: IFluidErrorBase & OdspError, clientId?: string) => {\n if (clientId === undefined || clientId === this.clientId) {\n this.logger.sendTelemetryEvent({ eventName: \"ServerDisconnect\", clientId: this.hasDetails ? this.clientId : undefined }, error);\n this.disconnect(error);\n }\n };\n\n protected async initialize(connectMessage: IConnect, timeout: number) {\n assert(!this.socketReference?.disconnected, 0x415 /* non-active socket */);\n\n if (this.enableMultiplexing) {\n // multiplex compatible early handlers\n this.earlyOpHandler = (messageDocumentId: string, msgs: ISequencedDocumentMessage[]) => {\n if (this.documentId === messageDocumentId) {\n this.queuedMessages.push(...msgs);\n }\n };\n\n this.earlySignalHandler = (msg: ISignalMessage, messageDocumentId?: string) => {\n if (messageDocumentId === undefined || messageDocumentId === this.documentId) {\n this.queuedSignals.push(msg);\n }\n };\n }\n\n this.socketReference!.on(\"disconnect\", this.disconnectHandler);\n\n this.socket.on(\"get_ops_response\", (result: IGetOpsResponse) => {\n const messages = result.messages;\n const data = this.getOpsMap.get(result.nonce);\n // Due to socket multiplexing, this client may not have asked for any data\n // If so, there it most likely does not need these ops (otherwise it already asked for them)\n // Also we may have deleted entry in this.getOpsMap due to too many requests and too slow response.\n // But not processing such result may push us into infinite loop of fast requests and dropping all responses\n if (data !== undefined || result.nonce.indexOf(this.requestOpsNoncePrefix) === 0) {\n this.getOpsMap.delete(result.nonce);\n const common = {\n eventName: \"GetOps\",\n // We need nonce only to pair with GetOpsTooMany events, i.e. when record was deleted\n nonce: data === undefined ? result.nonce : undefined,\n code: result.code,\n from: data?.from,\n to: data?.to,\n duration: data === undefined ? undefined : performance.now() - data.start,\n };\n if (messages !== undefined && messages.length > 0) {\n this.logger.sendPerformanceEvent({\n ...common,\n first: messages[0].sequenceNumber,\n last: messages[messages.length - 1].sequenceNumber,\n length: messages.length,\n });\n this.emit(\"op\", this.documentId, messages);\n } else {\n this.logger.sendPerformanceEvent({\n ...common,\n length: 0,\n });\n }\n }\n });\n\n this.socket.on(\"flush_ops_response\", (result: IFlushOpsResponse) => {\n if (this.flushOpNonce === result.nonce) {\n const seq = result.lastPersistedSequenceNumber;\n let category: \"generic\" | \"error\" = \"generic\";\n if (result.lastPersistedSequenceNumber === undefined || result.code !== 200) {\n switch (result.code) {\n case 409:\n case 429:\n category = \"error\";\n break;\n case 204:\n break;\n default:\n category = \"error\";\n break;\n }\n }\n this.logger.sendTelemetryEvent({\n eventName: \"FlushResult\",\n code: result.code,\n sequenceNumber: seq,\n category,\n });\n this.flushDeferred!.resolve(result);\n this.flushDeferred = undefined;\n this.flushOpNonce = undefined;\n }\n });\n\n await super.initialize(connectMessage, timeout);\n }\n\n protected addTrackedListener(event: string, listener: (...args: any[]) => void) {\n // override some event listeners in order to support multiple documents/clients over the same websocket\n switch (event) {\n case \"op\":\n // per document op handling\n super.addTrackedListener(event, (documentId: string, msgs: ISequencedDocumentMessage[]) => {\n if (!this.enableMultiplexing || this.documentId === documentId) {\n listener(documentId, msgs);\n }\n });\n break;\n\n case \"signal\":\n // per document signal handling\n super.addTrackedListener(event, (msg: ISignalMessage, documentId?: string) => {\n if (!this.enableMultiplexing || !documentId || documentId === this.documentId) {\n listener(msg, documentId);\n }\n });\n break;\n\n case \"nack\":\n // per client / document nack handling\n super.addTrackedListener(event, (clientIdOrDocumentId: string, nacks: INack[]) => {\n const handle = clientIdOrDocumentId.length === 0 ||\n clientIdOrDocumentId === this.documentId ||\n (clientIdOrDocumentId === this.clientId);\n const { code, type, message, retryAfter } = nacks[0]?.content ?? {};\n this.logger.sendTelemetryEvent({\n eventName: \"ServerNack\",\n code,\n type,\n message,\n retryAfterSeconds: retryAfter,\n clientId: this.clientId,\n handle,\n });\n if (handle) {\n this.emit(\"nack\", clientIdOrDocumentId, nacks);\n }\n });\n break;\n\n default:\n super.addTrackedListener(event, listener);\n break;\n }\n }\n\n /**\n * Critical path where we need to also close the socket for an error.\n * @param error - Error causing the socket to close.\n */\n protected closeSocket(error: IAnyDriverError) {\n const socket = this.socketReference;\n assert(socket !== undefined, 0x416 /* reentrancy not supported in close socket */);\n socket.closeSocket(error);\n assert(this.socketReference === undefined, 0x417 /* disconnect flow did not work correctly */);\n }\n\n /**\n * Disconnect from the websocket\n */\n protected disconnectCore() {\n const socket = this.socketReference;\n assert(socket !== undefined, 0x0a2 /* \"reentrancy not supported!\" */);\n this.socketReference = undefined;\n\n socket.off(\"disconnect\", this.disconnectHandler);\n if (this.hasDetails) {\n // tell the server we are disconnecting this client from the document\n this.socket.emit(\"disconnect_document\", this.clientId, this.documentId);\n }\n\n socket.removeSocketIoReference();\n }\n}\n"]}
1
+ {"version":3,"file":"odspDocumentDeltaConnection.js","sourceRoot":"","sources":["../src/odspDocumentDeltaConnection.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 { ITelemetryLogger, IEvent } from \"@fluidframework/common-definitions\";\nimport { assert, performance, Deferred, TypedEventEmitter } from \"@fluidframework/common-utils\";\nimport { DocumentDeltaConnection } from \"@fluidframework/driver-base\";\nimport { IAnyDriverError } from \"@fluidframework/driver-definitions\";\nimport { OdspError } from \"@fluidframework/odsp-driver-definitions\";\nimport { IFluidErrorBase, loggerToMonitoringContext } from \"@fluidframework/telemetry-utils\";\nimport {\n IClient,\n IConnect,\n INack,\n ISequencedDocumentMessage,\n ISignalMessage,\n} from \"@fluidframework/protocol-definitions\";\nimport { Socket, io as SocketIOClientStatic } from \"socket.io-client\";\nimport { v4 as uuid } from \"uuid\";\nimport { createGenericNetworkError } from \"@fluidframework/driver-utils\";\nimport { IOdspSocketError, IGetOpsResponse, IFlushOpsResponse } from \"./contracts\";\nimport { EpochTracker } from \"./epochTracker\";\nimport { errorObjectFromSocketError } from \"./odspError\";\nimport { pkgVersion } from \"./packageVersion\";\n\nconst protocolVersions = [\"^0.4.0\", \"^0.3.0\", \"^0.2.0\", \"^0.1.0\"];\nconst feature_get_ops = \"api_get_ops\";\nconst feature_flush_ops = \"api_flush_ops\";\n\nexport interface FlushResult {\n lastPersistedSequenceNumber?: number;\n retryAfter?: number;\n}\n\n// How long to wait before disconnecting the socket after the last reference is removed\n// This allows reconnection after receiving a nack to be smooth\nconst socketReferenceBufferTime = 2000;\n\ninterface ISocketEvents extends IEvent {\n (event: \"disconnect\", listener: (error: IFluidErrorBase & OdspError, clientId?: string) => void);\n}\n\nclass SocketReference extends TypedEventEmitter<ISocketEvents> {\n private references: number = 1;\n private delayDeleteTimeout: ReturnType<typeof setTimeout> | undefined;\n private _socket: Socket | undefined;\n\n // When making decisions about socket reuse, we do not reuse disconnected socket.\n // But we want to differentiate the following case from disconnected case:\n // Socket that never connected and never failed, it's in \"attempting to connect\" mode\n // such sockets should be reused, despite socket.disconnected === true\n private isPendingInitialConnection = true;\n\n // Map of all existing socket io sockets. [url, tenantId, documentId] -> socket\n private static readonly socketIoSockets: Map<string, SocketReference> = new Map();\n\n public static find(key: string, logger: ITelemetryLogger) {\n const socketReference = SocketReference.socketIoSockets.get(key);\n\n // Verify the socket is healthy before reusing it\n if (socketReference?.disconnected) {\n // The socket is in a bad state. fully remove the reference\n socketReference.closeSocket();\n return undefined;\n }\n\n if (socketReference) {\n // Clear the pending deletion if there is one\n socketReference.clearTimer();\n socketReference.references++;\n }\n\n return socketReference;\n }\n\n /**\n * Removes a reference for the given key\n * Once the ref count hits 0, the socket is disconnected and removed\n */\n public removeSocketIoReference() {\n assert(this.references > 0, 0x09f /* \"No more socketIO refs to remove!\" */);\n this.references--;\n\n // see comment in disconnected() getter\n this.isPendingInitialConnection = false;\n\n if (this.disconnected) {\n this.closeSocket();\n return;\n }\n\n if (this.references === 0 && this.delayDeleteTimeout === undefined) {\n this.delayDeleteTimeout = setTimeout(() => {\n // We should not get here with active users.\n assert(this.references === 0, 0x0a0 /* \"Unexpected socketIO references on timeout\" */);\n this.closeSocket();\n }, socketReferenceBufferTime);\n }\n }\n\n public get socket() {\n if (!this._socket) {\n throw new Error(`Invalid socket for key \"${this.key}`);\n }\n return this._socket;\n }\n\n public constructor(public readonly key: string, socket: Socket) {\n super();\n\n this._socket = socket;\n assert(!SocketReference.socketIoSockets.has(key), 0x220 /* \"socket key collision\" */);\n SocketReference.socketIoSockets.set(key, this);\n\n // Server sends this event when it wants to disconnect a particular client in which case the client id would\n // be present or if it wants to disconnect all the clients. The server always closes the socket in case all\n // clients needs to be disconnected. So fully remove the socket reference in this case.\n socket.on(\"server_disconnect\", (socketError: IOdspSocketError, clientId?: string) => {\n // Treat all errors as recoverable, and rely on joinSession / reconnection flow to\n // filter out retryable vs. non-retryable cases.\n const error = errorObjectFromSocketError(socketError, \"server_disconnect\");\n error.addTelemetryProperties({ disconnectClientId: clientId });\n error.canRetry = true;\n\n // see comment in disconnected() getter\n // Setting it here to ensure socket reuse does not happen if new request to connect\n // comes in from \"disconnect\" listener below, before we close socket.\n this.isPendingInitialConnection = false;\n\n if (clientId === undefined) {\n // We could first raise \"disconnect\" event, but that may result in socket reuse due to\n // new connection comming in. So, it's better to have more explicit flow to make it impossible.\n this.closeSocket(error);\n } else {\n this.emit(\"disconnect\", error, clientId);\n }\n });\n }\n\n private clearTimer() {\n if (this.delayDeleteTimeout !== undefined) {\n clearTimeout(this.delayDeleteTimeout);\n this.delayDeleteTimeout = undefined;\n }\n }\n\n public closeSocket(error?: IAnyDriverError) {\n if (!this._socket) { return; }\n\n this.clearTimer();\n\n assert(SocketReference.socketIoSockets.get(this.key) === this,\n 0x0a1 /* \"Socket reference set unexpectedly does not point to this socket!\" */);\n\n // First, remove socket to ensure no socket reuse is possible.\n SocketReference.socketIoSockets.delete(this.key);\n\n // Block access to socket. From now on, calls like flush() or requestOps()\n // Disconnect flow should be synchronous and result in system fully forgetting about this connection / socket.\n const socket = this._socket;\n this._socket = undefined;\n\n // Let all connections know they need to go through disconnect flow.\n this.emit(\"disconnect\",\n error ?? createGenericNetworkError(\n \"Socket closed without error\",\n { canRetry: true },\n { driverVersion: pkgVersion }),\n undefined /* clientId */);\n\n // We should not have any users now, assuming synchronous disconnect flow in response to\n // \"disconnect\" event\n assert(this.references === 0, 0x412 /* Nobody should be connected to this socket at this point! */);\n\n socket.disconnect();\n }\n\n public get disconnected() {\n if (this._socket === undefined) { return true; }\n if (this.socket.connected) { return false; }\n\n // We have a socket that is not connected. Possible cases:\n // 1) It was connected some time ago and lost connection. We do not want to reuse it.\n // 2) It failed to connect (was never connected).\n // 3) It was just created and never had a chance to connect - connection is in process.\n // We have to differentiate 1 from 2-3 (specifically 1 & 3) in order to be able to reuse socket in #3.\n // We will use the fact that socket had some activity. I.e. if socket disconnected, or client stopped using\n // socket, then removeSocketIoReference() will be called for it, and it will be the indiction that it's not #3.\n return !this.isPendingInitialConnection;\n }\n}\n\n/**\n * Represents a connection to a stream of delta updates\n */\nexport class OdspDocumentDeltaConnection extends DocumentDeltaConnection {\n /**\n * Create a OdspDocumentDeltaConnection\n * If url #1 fails to connect, will try url #2 if applicable.\n *\n * @param tenantId - the ID of the tenant\n * @param documentId - document ID\n * @param token - authorization token for storage service\n * @param client - information about the client\n * @param mode - mode of the client\n * @param url - websocket URL\n * @param telemetryLogger - optional telemetry logger\n * @param timeoutMs - time limit on making the connection\n * @param epochTracker - track epoch changes\n * @param socketReferenceKeyPrefix - (optional) prefix to isolate socket reuse cache\n */\n public static async create(\n tenantId: string,\n documentId: string,\n token: string | null,\n client: IClient,\n url: string,\n telemetryLogger: ITelemetryLogger,\n timeoutMs: number,\n epochTracker: EpochTracker,\n socketReferenceKeyPrefix: string | undefined): Promise<OdspDocumentDeltaConnection> {\n const mc = loggerToMonitoringContext(telemetryLogger);\n\n // enable multiplexing when the websocket url does not include the tenant/document id\n const parsedUrl = new URL(url);\n const enableMultiplexing = !parsedUrl.searchParams.has(\"documentId\") && !parsedUrl.searchParams.has(\"tenantId\");\n\n // do not include the specific tenant/doc id in the ref key when multiplexing\n // this will allow multiple documents to share the same websocket connection\n const key = socketReferenceKeyPrefix ? `${socketReferenceKeyPrefix},${url}` : url;\n const socketReferenceKey = enableMultiplexing ? key : `${key},${tenantId},${documentId}`;\n\n const socketReference = OdspDocumentDeltaConnection.getOrCreateSocketIoReference(\n timeoutMs, socketReferenceKey, url, enableMultiplexing, tenantId, documentId, telemetryLogger);\n\n const socket = socketReference.socket;\n\n const connectMessage: IConnect = {\n client,\n id: documentId,\n mode: client.mode,\n tenantId,\n token, // Token is going to indicate tenant level information, etc...\n versions: protocolVersions,\n driverVersion: pkgVersion,\n nonce: uuid(),\n epoch: epochTracker.fluidEpoch,\n relayUserAgent: [client.details.environment, ` driverVersion:${pkgVersion}`].join(\";\"),\n };\n\n // Reference to this client supporting get_ops flow.\n connectMessage.supportedFeatures = { };\n if (mc.config.getBoolean(\"Fluid.Driver.Odsp.GetOpsEnabled\") !== false) {\n connectMessage.supportedFeatures[feature_get_ops] = true;\n }\n\n const deltaConnection = new OdspDocumentDeltaConnection(\n socket,\n documentId,\n socketReference,\n telemetryLogger,\n enableMultiplexing);\n\n try {\n await deltaConnection.initialize(connectMessage, timeoutMs);\n await epochTracker.validateEpochFromPush(deltaConnection.details);\n } catch (errorObject: any) {\n if (errorObject !== null && typeof errorObject === \"object\") {\n // We have to special-case error types here in terms of what is re-triable.\n // These errors have to re-retried, we just need new joinSession result to connect to right server:\n // 400: Invalid tenant or document id. The WebSocket is connected to a different document\n // Document is full (with retryAfter)\n // 404: Invalid document. The document \\\"local/w1-...\\\" does not exist\n // But this has to stay not-retriable:\n // 406: Unsupported client protocol. This path is the only gatekeeper, have to fail!\n // 409: Epoch Version Mismatch. Client epoch and server epoch does not match, so app needs\n // to be refreshed.\n // This one is fine either way\n // 401/403: Code will retry once with new token either way, then it becomes fatal - on this path\n // and on join Session path.\n // 501: (Fluid not enabled): this is fine either way, as joinSession is gatekeeper\n if (errorObject.statusCode === 400 || errorObject.statusCode === 404) {\n errorObject.canRetry = true;\n }\n }\n throw errorObject;\n }\n\n return deltaConnection;\n }\n\n private socketReference: SocketReference | undefined;\n\n private readonly requestOpsNoncePrefix: string;\n private pushCallCounter = 0;\n private readonly getOpsMap: Map<string, { start: number; from: number; to: number; }> = new Map();\n private flushOpNonce: string | undefined;\n private flushDeferred: Deferred<FlushResult> | undefined;\n\n /**\n * Error raising for socket.io issues\n */\n protected createErrorObject(handler: string, error?: any, canRetry = true): IAnyDriverError {\n // Note: we suspect the incoming error object is either:\n // - a socketError: add it to the OdspError object for driver to be able to parse it and reason over it.\n // - anything else: let base class handle it\n return canRetry && Number.isInteger(error?.code) && typeof error?.message === \"string\"\n ? errorObjectFromSocketError(error as IOdspSocketError, handler)\n : super.createErrorObject(handler, error, canRetry);\n }\n\n /**\n * Gets or create a socket io connection for the given key\n */\n private static getOrCreateSocketIoReference(\n timeoutMs: number,\n key: string,\n url: string,\n enableMultiplexing: boolean,\n tenantId: string,\n documentId: string,\n logger: ITelemetryLogger,\n ): SocketReference {\n const existingSocketReference = SocketReference.find(key, logger);\n if (existingSocketReference) {\n return existingSocketReference;\n }\n\n const query = enableMultiplexing ? undefined : { documentId, tenantId };\n\n const socket = SocketIOClientStatic(\n url,\n {\n multiplex: false, // Don't rely on socket.io built-in multiplexing\n query,\n reconnection: false,\n transports: [\"websocket\"],\n timeout: timeoutMs,\n });\n\n return new SocketReference(key, socket);\n }\n\n /**\n * @param socket - websocket to be used\n * @param documentId - ID of the document\n * @param details - details of the websocket connection\n * @param socketReferenceKey - socket reference key\n * @param enableMultiplexing - If the websocket is multiplexing multiple documents\n */\n private constructor(\n socket: Socket,\n documentId: string,\n socketReference: SocketReference,\n logger: ITelemetryLogger,\n private readonly enableMultiplexing?: boolean,\n ) {\n super(socket, documentId, logger);\n this.socketReference = socketReference;\n this.requestOpsNoncePrefix = `${uuid()}-`;\n }\n\n /**\n * Retrieves ops from PUSH\n * @param from - inclusive\n * @param to - exclusive\n * @returns ops retrieved\n */\n public requestOps(from: number, to: number) {\n assert(!this.socketReference?.disconnected, 0x413 /* non-active socket */);\n\n // Given that to is exclusive, we should be asking for at least something!\n assert(to > from, 0x272 /* \"empty request\" */);\n\n // PUSH may disable this functionality\n // back-compat: remove cast to any once latest version of IConnected is consumed\n if ((this.details as any).supportedFeatures?.[feature_get_ops] !== true) {\n return;\n }\n\n this.pushCallCounter++;\n const nonce = `${this.requestOpsNoncePrefix}${this.pushCallCounter}`;\n const start = performance.now();\n\n // We may keep keep accumulating memory for nothing, if we are not getting responses.\n // Note that we should not have overlapping requests, as DeltaManager allows only one\n // outstanding request to storage, and that's the only way to get here.\n // But requests could be cancelled, and thus overlapping requests might be in the picture\n // If it happens, we do not care about stale requests.\n // So track some number of requests, but log if we get too many in flight - that likely\n // indicates an error somewhere.\n if (this.getOpsMap.size >= 5) {\n let time = start;\n let key: string | undefined;\n for (const [keyCandidate, value] of this.getOpsMap.entries()) {\n if (value.start <= time || key === undefined) {\n time = value.start;\n key = keyCandidate;\n }\n }\n const payloadToDelete = this.getOpsMap.get(key!)!;\n this.logger.sendErrorEvent({\n eventName: \"GetOpsTooMany\",\n nonce,\n from: payloadToDelete.from,\n to: payloadToDelete.to,\n length: payloadToDelete.to - payloadToDelete.from,\n duration: performance.now() - payloadToDelete.start,\n });\n this.getOpsMap.delete(key!);\n }\n this.getOpsMap.set(\n nonce,\n {\n start,\n from,\n to,\n },\n );\n this.socket.emit(\"get_ops\", this.clientId, {\n nonce,\n from,\n to: to - 1,\n });\n }\n\n public async flush(): Promise<FlushResult> {\n assert(!this.socketReference?.disconnected, 0x414 /* non-active socket */);\n\n // back-compat: remove cast to any once latest version of IConnected is consumed\n if ((this.details as any).supportedFeatures?.[feature_flush_ops] !== true) {\n // Once single-commit summary is enabled end-to-end, flush support is a must!\n // The only alternative is change in design where SPO fetches ops from PUSH OR\n // summary includes required ops and SPO has some validation mechanism to ensure\n // they are not forged by client.\n // If design changes, we can reconsider it, but right now it's non-recoverable failure.\n this.logger.sendErrorEvent({ eventName: \"FlushOpsNotSupported\" });\n throw new Error(\"flush() API is not supported by PUSH, required for single-commit summaries\");\n }\n\n this.pushCallCounter++;\n const nonce = `${this.requestOpsNoncePrefix}${this.pushCallCounter}`;\n // There should be only one flush ops in flight, kicked out by upload summary workflow\n // That said, it could timeout and request could be repeated, so theoretically we can\n // get overlapping requests, but it should be very rare\n if (this.flushDeferred !== undefined) {\n this.logger.sendErrorEvent({ eventName: \"FlushOpsTooMany\" });\n this.flushDeferred.reject(\"process involving flush() was cancelled OR unsupported concurrency\");\n }\n this.socket.emit(\"flush_ops\", this.clientId, { nonce });\n\n this.flushOpNonce = nonce;\n this.flushDeferred = new Deferred<FlushResult>();\n return this.flushDeferred.promise;\n }\n\n protected disconnectHandler = (error: IFluidErrorBase & OdspError, clientId?: string) => {\n if (clientId === undefined || clientId === this.clientId) {\n this.logger.sendTelemetryEvent({ eventName: \"ServerDisconnect\", clientId: this.hasDetails ? this.clientId : undefined }, error);\n this.disconnect(error);\n }\n };\n\n protected async initialize(connectMessage: IConnect, timeout: number) {\n assert(!this.socketReference?.disconnected, 0x415 /* non-active socket */);\n\n if (this.enableMultiplexing) {\n // multiplex compatible early handlers\n this.earlyOpHandler = (messageDocumentId: string, msgs: ISequencedDocumentMessage[]) => {\n if (this.documentId === messageDocumentId) {\n this.queuedMessages.push(...msgs);\n }\n };\n\n this.earlySignalHandler = (msg: ISignalMessage, messageDocumentId?: string) => {\n if (messageDocumentId === undefined || messageDocumentId === this.documentId) {\n this.queuedSignals.push(msg);\n }\n };\n }\n\n this.socketReference!.on(\"disconnect\", this.disconnectHandler);\n\n this.socket.on(\"get_ops_response\", (result: IGetOpsResponse) => {\n const messages = result.messages;\n const data = this.getOpsMap.get(result.nonce);\n // Due to socket multiplexing, this client may not have asked for any data\n // If so, there it most likely does not need these ops (otherwise it already asked for them)\n // Also we may have deleted entry in this.getOpsMap due to too many requests and too slow response.\n // But not processing such result may push us into infinite loop of fast requests and dropping all responses\n if (data !== undefined || result.nonce.indexOf(this.requestOpsNoncePrefix) === 0) {\n this.getOpsMap.delete(result.nonce);\n const common = {\n eventName: \"GetOps\",\n // We need nonce only to pair with GetOpsTooMany events, i.e. when record was deleted\n nonce: data === undefined ? result.nonce : undefined,\n code: result.code,\n from: data?.from,\n to: data?.to,\n duration: data === undefined ? undefined : performance.now() - data.start,\n };\n if (messages !== undefined && messages.length > 0) {\n this.logger.sendPerformanceEvent({\n ...common,\n first: messages[0].sequenceNumber,\n last: messages[messages.length - 1].sequenceNumber,\n length: messages.length,\n });\n this.emit(\"op\", this.documentId, messages);\n } else {\n this.logger.sendPerformanceEvent({\n ...common,\n length: 0,\n });\n }\n }\n });\n\n this.socket.on(\"flush_ops_response\", (result: IFlushOpsResponse) => {\n if (this.flushOpNonce === result.nonce) {\n const seq = result.lastPersistedSequenceNumber;\n let category: \"generic\" | \"error\" = \"generic\";\n if (result.lastPersistedSequenceNumber === undefined || result.code !== 200) {\n switch (result.code) {\n case 409:\n case 429:\n category = \"error\";\n break;\n case 204:\n break;\n default:\n category = \"error\";\n break;\n }\n }\n this.logger.sendTelemetryEvent({\n eventName: \"FlushResult\",\n code: result.code,\n sequenceNumber: seq,\n category,\n });\n this.flushDeferred!.resolve(result);\n this.flushDeferred = undefined;\n this.flushOpNonce = undefined;\n }\n });\n\n await super.initialize(connectMessage, timeout);\n }\n\n protected addTrackedListener(event: string, listener: (...args: any[]) => void) {\n // override some event listeners in order to support multiple documents/clients over the same websocket\n switch (event) {\n case \"op\":\n // per document op handling\n super.addTrackedListener(event, (documentId: string, msgs: ISequencedDocumentMessage[]) => {\n if (!this.enableMultiplexing || this.documentId === documentId) {\n listener(documentId, msgs);\n }\n });\n break;\n\n case \"signal\":\n // per document signal handling\n super.addTrackedListener(event, (msg: ISignalMessage, documentId?: string) => {\n if (!this.enableMultiplexing || !documentId || documentId === this.documentId) {\n listener(msg, documentId);\n }\n });\n break;\n\n case \"nack\":\n // per client / document nack handling\n super.addTrackedListener(event, (clientIdOrDocumentId: string, nacks: INack[]) => {\n const handle = clientIdOrDocumentId.length === 0 ||\n clientIdOrDocumentId === this.documentId ||\n (clientIdOrDocumentId === this.clientId);\n const { code, type, message, retryAfter } = nacks[0]?.content ?? {};\n this.logger.sendTelemetryEvent({\n eventName: \"ServerNack\",\n code,\n type,\n message,\n retryAfterSeconds: retryAfter,\n clientId: this.clientId,\n handle,\n });\n if (handle) {\n this.emit(\"nack\", clientIdOrDocumentId, nacks);\n }\n });\n break;\n\n default:\n super.addTrackedListener(event, listener);\n break;\n }\n }\n\n /**\n * Critical path where we need to also close the socket for an error.\n * @param error - Error causing the socket to close.\n */\n protected closeSocket(error: IAnyDriverError) {\n const socket = this.socketReference;\n assert(socket !== undefined, 0x416 /* reentrancy not supported in close socket */);\n socket.closeSocket(error);\n assert(this.socketReference === undefined, 0x417 /* disconnect flow did not work correctly */);\n }\n\n /**\n * Disconnect from the websocket\n */\n protected disconnectCore() {\n const socket = this.socketReference;\n assert(socket !== undefined, 0x0a2 /* \"reentrancy not supported!\" */);\n this.socketReference = undefined;\n\n socket.off(\"disconnect\", this.disconnectHandler);\n if (this.hasDetails) {\n // tell the server we are disconnecting this client from the document\n this.socket.emit(\"disconnect_document\", this.clientId, this.documentId);\n }\n\n socket.removeSocketIoReference();\n }\n}\n"]}