@fluidframework/odsp-driver 0.54.0-47413 → 0.54.3
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/dist/odspDocumentDeltaConnection.d.ts.map +1 -1
- package/dist/odspDocumentDeltaConnection.js +6 -2
- package/dist/odspDocumentDeltaConnection.js.map +1 -1
- package/dist/odspUtils.d.ts.map +1 -1
- package/dist/odspUtils.js +4 -1
- package/dist/odspUtils.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/lib/odspDocumentDeltaConnection.d.ts.map +1 -1
- package/lib/odspDocumentDeltaConnection.js +6 -2
- package/lib/odspDocumentDeltaConnection.js.map +1 -1
- package/lib/odspUtils.d.ts.map +1 -1
- package/lib/odspUtils.js +5 -2
- package/lib/odspUtils.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/package.json +7 -7
- package/src/odspDocumentDeltaConnection.ts +7 -3
- package/src/odspUtils.ts +13 -3
- package/src/packageVersion.ts +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"odspDocumentDeltaConnection.d.ts","sourceRoot":"","sources":["../src/odspDocumentDeltaConnection.ts"],"names":[],"mappings":"AAAA;;;GAGG;;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,oCAAoC,CAAC;AAE9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,
|
|
1
|
+
{"version":3,"file":"odspDocumentDeltaConnection.d.ts","sourceRoot":"","sources":["../src/odspDocumentDeltaConnection.ts"],"names":[],"mappings":"AAAA;;;GAGG;;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,oCAAoC,CAAC;AAE9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAA6B,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAC1F,OAAO,EACH,OAAO,EACP,QAAQ,EAIX,MAAM,sCAAsC,CAAC;AAG9C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAQ9C,MAAM,WAAW,WAAW;IACxB,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAMD,MAAM,WAAW,aAAc,SAAQ,MAAM;IACzC,CAAC,KAAK,EAAE,mBAAmB,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,KAAK,IAAI,OAAE;CACrF;AAwID;;GAEG;AACH,qBAAa,2BAA4B,SAAQ,uBAAuB;IAoKhE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC;IAnKxC;;;;;;;;;;;;;;;OAeG;WACiB,MAAM,CACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,EAAE,EAAE,oBAAoB,EACxB,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;IAsEvF,OAAO,CAAC,eAAe,CAA8B;IAErD,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAS;IAC/C,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAuE;IACjG,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,WAAW;IAWvF;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,4BAA4B;IA8B3C;;;;;;OAMG;IACH,OAAO;IAYP;;;;;OAKG;IACK,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM;IAwD9B,KAAK,IAAI,OAAO,CAAC,WAAW,CAAC;IA4B1C,SAAS,CAAC,uBAAuB,UAAW,YAAY,GAAG,SAAS,UAElE;cAEc,UAAU,CAAC,cAAc,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM;IAqFpE,SAAS,CAAC,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI;IAsC9E;;OAEG;IACH,SAAS,CAAC,UAAU,CAAC,mBAAmB,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG;CAejE"}
|
|
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
7
|
exports.OdspDocumentDeltaConnection = void 0;
|
|
8
8
|
const common_utils_1 = require("@fluidframework/common-utils");
|
|
9
9
|
const driver_base_1 = require("@fluidframework/driver-base");
|
|
10
|
+
const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
|
|
10
11
|
const uuid_1 = require("uuid");
|
|
11
12
|
const odspError_1 = require("./odspError");
|
|
12
13
|
const packageVersion_1 = require("./packageVersion");
|
|
@@ -168,6 +169,7 @@ class OdspDocumentDeltaConnection extends driver_base_1.DocumentDeltaConnection
|
|
|
168
169
|
* @param socketReferenceKeyPrefix - (optional) prefix to isolate socket reuse cache
|
|
169
170
|
*/
|
|
170
171
|
static async create(tenantId, documentId, token, io, client, url, telemetryLogger, timeoutMs, epochTracker, socketReferenceKeyPrefix) {
|
|
172
|
+
const mc = telemetry_utils_1.loggerToMonitoringContext(telemetryLogger);
|
|
171
173
|
// enable multiplexing when the websocket url does not include the tenant/document id
|
|
172
174
|
const parsedUrl = new URL(url);
|
|
173
175
|
const enableMultiplexing = !parsedUrl.searchParams.has("documentId") && !parsedUrl.searchParams.has("tenantId");
|
|
@@ -189,8 +191,10 @@ class OdspDocumentDeltaConnection extends driver_base_1.DocumentDeltaConnection
|
|
|
189
191
|
relayUserAgent: [client.details.environment, ` driverVersion:${packageVersion_1.pkgVersion}`].join(";"),
|
|
190
192
|
};
|
|
191
193
|
// Reference to this client supporting get_ops flow.
|
|
192
|
-
|
|
193
|
-
|
|
194
|
+
connectMessage.supportedFeatures = {};
|
|
195
|
+
if (mc.config.getBoolean("Fluid.Driver.Odsp.GetOpsEnabled") === true) {
|
|
196
|
+
connectMessage.supportedFeatures[feature_get_ops] = true;
|
|
197
|
+
}
|
|
194
198
|
const deltaConnection = new OdspDocumentDeltaConnection(socket, documentId, socketReference, telemetryLogger, enableMultiplexing);
|
|
195
199
|
try {
|
|
196
200
|
await deltaConnection.initialize(connectMessage, timeoutMs);
|
|
@@ -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 { DriverError } from \"@fluidframework/driver-definitions\";\nimport { OdspError } from \"@fluidframework/odsp-driver-definitions\";\nimport { LoggingError } from \"@fluidframework/telemetry-utils\";\nimport {\n IClient,\n IConnect,\n INack,\n ISequencedDocumentMessage,\n ISignalMessage,\n} from \"@fluidframework/protocol-definitions\";\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\nexport interface ISocketEvents extends IEvent {\n (event: \"server_disconnect\", listener: (error: LoggingError & OdspError) => void);\n}\n\nclass SocketReference extends TypedEventEmitter<ISocketEvents> {\n private references: number = 1;\n private delayDeleteTimeout: ReturnType<typeof setTimeout> | undefined;\n private _socket: SocketIOClient.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 && 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 * @param key - socket reference key\n * @param isFatalError - true if the socket reference should be removed immediately due to a fatal error\n */\n public removeSocketIoReference(isFatalError: boolean) {\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 (isFatalError || 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: SocketIOClient.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 // The server always closes the socket after sending this message\n // fully remove the socket reference now\n socket.on(\"server_disconnect\", (socketError: IOdspSocketError) => {\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.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 this.emit(\"server_disconnect\", error);\n this.closeSocket();\n });\n }\n\n private clearTimer() {\n if (this.delayDeleteTimeout !== undefined) {\n clearTimeout(this.delayDeleteTimeout);\n this.delayDeleteTimeout = undefined;\n }\n }\n\n private closeSocket() {\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 SocketReference.socketIoSockets.delete(this.key);\n\n const socket = this._socket;\n this._socket = undefined;\n\n // Delay closing socket, to make sure all users of socket observe the same event that causes\n // this instance to close, and thus properly record reason for closure.\n // All event raising is synchronous, so clients will have a chance to react before socket is\n // closed without any extra data on why it was closed.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n Promise.resolve().then(() => { socket.disconnect(); });\n }\n\n private 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: SocketIOClientStatic,\n client: IClient,\n url: string,\n telemetryLogger: ITelemetryLogger,\n timeoutMs: number,\n epochTracker: EpochTracker,\n socketReferenceKeyPrefix: string | undefined): Promise<OdspDocumentDeltaConnection> {\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 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 // back-compat: remove cast to any once new definition of IConnect comes through.\n (connectMessage as any).supportedFeatures = { [feature_get_ops]: true };\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) {\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): DriverError {\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 if (canRetry && Number.isInteger(error?.code) && typeof error?.message === \"string\") {\n return errorObjectFromSocketError(error as IOdspSocketError, handler) as DriverError;\n } else {\n return super.createErrorObject(handler, error, canRetry);\n }\n }\n\n /**\n * Gets or create a socket io connection for the given key\n */\n private static getOrCreateSocketIoReference(\n io: 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: SocketIOClient.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 // 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 // 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 serverDisconnectHandler = (error: LoggingError & OdspError) => {\n this.disposeCore(true, error);\n };\n\n protected async initialize(connectMessage: IConnect, timeout: number) {\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!.once(\"server_disconnect\", this.serverDisconnectHandler);\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, message: INack[]) => {\n if (clientIdOrDocumentId.length === 0 ||\n clientIdOrDocumentId === this.documentId ||\n (this.hasDetails && clientIdOrDocumentId === this.clientId)) {\n this.emit(\"nack\", clientIdOrDocumentId, message);\n }\n });\n break;\n\n default:\n super.addTrackedListener(event, listener);\n break;\n }\n }\n\n /**\n * Disconnect from the websocket\n */\n protected disconnect(socketProtocolError: boolean, reason: any) {\n const socket = this.socketReference;\n assert(socket !== undefined, 0x0a2 /* \"reentrancy not supported!\" */);\n this.socketReference = undefined;\n\n this.socket.off(\"server_disconnect\", this.serverDisconnectHandler);\n\n if (!socketProtocolError && 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(socketProtocolError);\n this.emit(\"disconnect\", reason);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"odspDocumentDeltaConnection.js","sourceRoot":"","sources":["../src/odspDocumentDeltaConnection.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAGH,+DAAgG;AAChG,6DAAsE;AAGtE,qEAA0F;AAQ1F,+BAAkC;AAGlC,2CAAyD;AACzD,qDAA8C;AAE9C,MAAM,gBAAgB,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAClE,MAAM,eAAe,GAAG,aAAa,CAAC;AACtC,MAAM,iBAAiB,GAAG,eAAe,CAAC;AAO1C,uFAAuF;AACvF,+DAA+D;AAC/D,MAAM,yBAAyB,GAAG,IAAI,CAAC;AAMvC,MAAM,eAAgB,SAAQ,gCAAgC;IAmE1D,YAAmC,GAAW,EAAE,MAA6B;QACzE,KAAK,EAAE,CAAC;QADuB,QAAG,GAAH,GAAG,CAAQ;QAlEtC,eAAU,GAAW,CAAC,CAAC;QAI/B,iFAAiF;QACjF,0EAA0E;QAC1E,qFAAqF;QACrF,sEAAsE;QAC9D,+BAA0B,GAAG,IAAI,CAAC;QA6DtC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,qBAAM,CAAC,CAAC,eAAe,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,4BAA4B,CAAC,CAAC;QACtF,eAAe,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAE/C,iEAAiE;QACjE,wCAAwC;QACxC,MAAM,CAAC,EAAE,CAAC,mBAAmB,EAAE,CAAC,WAA6B,EAAE,EAAE;YAC7D,kFAAkF;YAClF,gDAAgD;YAChD,MAAM,KAAK,GAAG,sCAA0B,CAAC,WAAW,EAAE,mBAAmB,CAAC,CAAC;YAC3E,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC;YAEtB,uCAAuC;YACvC,mFAAmF;YACnF,qEAAqE;YACrE,IAAI,CAAC,0BAA0B,GAAG,KAAK,CAAC;YAExC,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,KAAK,CAAC,CAAC;YACtC,IAAI,CAAC,WAAW,EAAE,CAAC;QACvB,CAAC,CAAC,CAAC;IACP,CAAC;IA5EM,MAAM,CAAC,IAAI,CAAC,GAAW,EAAE,MAAwB;QACpD,MAAM,eAAe,GAAG,eAAe,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAEjE,iDAAiD;QACjD,IAAI,eAAe,IAAI,eAAe,CAAC,YAAY,EAAE;YACjD,2DAA2D;YAC3D,eAAe,CAAC,WAAW,EAAE,CAAC;YAC9B,OAAO,SAAS,CAAC;SACpB;QAED,IAAI,eAAe,EAAE;YACjB,6CAA6C;YAC7C,eAAe,CAAC,UAAU,EAAE,CAAC;YAC7B,eAAe,CAAC,UAAU,EAAE,CAAC;SAChC;QAED,OAAO,eAAe,CAAC;IAC3B,CAAC;IAED;;;;;OAKG;IACI,uBAAuB,CAAC,YAAqB;QAChD,qBAAM,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,EAAE,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAC5E,IAAI,CAAC,UAAU,EAAE,CAAC;QAElB,uCAAuC;QACvC,IAAI,CAAC,0BAA0B,GAAG,KAAK,CAAC;QAExC,IAAI,YAAY,IAAI,IAAI,CAAC,YAAY,EAAE;YACnC,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,OAAO;SACV;QAED,IAAI,IAAI,CAAC,UAAU,KAAK,CAAC,IAAI,IAAI,CAAC,kBAAkB,KAAK,SAAS,EAAE;YAChE,IAAI,CAAC,kBAAkB,GAAG,UAAU,CAAC,GAAG,EAAE;gBACtC,4CAA4C;gBAC5C,qBAAM,CAAC,IAAI,CAAC,UAAU,KAAK,CAAC,EAAE,KAAK,CAAC,iDAAiD,CAAC,CAAC;gBACvF,IAAI,CAAC,WAAW,EAAE,CAAC;YACvB,CAAC,EAAE,yBAAyB,CAAC,CAAC;SACjC;IACL,CAAC;IAED,IAAW,MAAM;QACb,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;YACf,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;SAC1D;QACD,OAAO,IAAI,CAAC,OAAO,CAAC;IACxB,CAAC;IA2BO,UAAU;QACd,IAAI,IAAI,CAAC,kBAAkB,KAAK,SAAS,EAAE;YACvC,YAAY,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;YACtC,IAAI,CAAC,kBAAkB,GAAG,SAAS,CAAC;SACvC;IACL,CAAC;IAEO,WAAW;QACf,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;YAAE,OAAO;SAAE;QAE9B,IAAI,CAAC,UAAU,EAAE,CAAC;QAElB,qBAAM,CAAC,eAAe,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,EACzD,KAAK,CAAC,wEAAwE,CAAC,CAAC;QACpF,eAAe,CAAC,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAEjD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC;QAC5B,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;QAEzB,4FAA4F;QAC5F,uEAAuE;QACvE,4FAA4F;QAC5F,sDAAsD;QACtD,mEAAmE;QACnE,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED,IAAY,YAAY;QACpB,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE;YAAE,OAAO,IAAI,CAAC;SAAE;QAChD,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE;YAAE,OAAO,KAAK,CAAC;SAAE;QAE5C,0DAA0D;QAC1D,qFAAqF;QACrF,iDAAiD;QACjD,uFAAuF;QACvF,sGAAsG;QACtG,2GAA2G;QAC3G,+GAA+G;QAC/G,OAAO,CAAC,IAAI,CAAC,0BAA0B,CAAC;IAC5C,CAAC;;AAxHD,+EAA+E;AACvD,+BAAe,GAAiC,IAAI,GAAG,EAAE,CAAC;AA0HtF;;GAEG;AACH,MAAa,2BAA4B,SAAQ,qCAAuB;IAwJpE;;;;;;OAMG;IACH,YACI,MAA6B,EAC7B,UAAkB,EAClB,eAAgC,EAChC,MAAwB,EACP,kBAA4B;QAE7C,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;QAFjB,uBAAkB,GAAlB,kBAAkB,CAAU;QAhEzC,oBAAe,GAAG,CAAC,CAAC;QACX,cAAS,GAA6D,IAAI,GAAG,EAAE,CAAC;QAgKvF,4BAAuB,GAAG,CAAC,KAA+B,EAAE,EAAE;YACpE,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAClC,CAAC,CAAC;QAhGE,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;QACvC,IAAI,CAAC,qBAAqB,GAAG,GAAG,SAAI,EAAE,GAAG,CAAC;IAC9C,CAAC;IAxKD;;;;;;;;;;;;;;;OAeG;IACI,MAAM,CAAC,KAAK,CAAC,MAAM,CACtB,QAAgB,EAChB,UAAkB,EAClB,KAAoB,EACpB,EAAwB,EACxB,MAAe,EACf,GAAW,EACX,eAAiC,EACjC,SAAiB,EACjB,YAA0B,EAC1B,wBAA4C;QAC5C,MAAM,EAAE,GAAG,2CAAyB,CAAC,eAAe,CAAC,CAAC;QAEtD,qFAAqF;QACrF,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,kBAAkB,GAAG,CAAC,SAAS,CAAC,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAEhH,6EAA6E;QAC7E,4EAA4E;QAC5E,MAAM,GAAG,GAAG,wBAAwB,CAAC,CAAC,CAAC,GAAG,wBAAwB,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;QAClF,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,QAAQ,IAAI,UAAU,EAAE,CAAC;QAEzF,MAAM,eAAe,GAAG,2BAA2B,CAAC,4BAA4B,CAC5E,EAAE,EAAE,SAAS,EAAE,kBAAkB,EAAE,GAAG,EAAE,kBAAkB,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC;QAEvG,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC;QAEtC,MAAM,cAAc,GAAa;YAC7B,MAAM;YACN,EAAE,EAAE,UAAU;YACd,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,QAAQ;YACR,KAAK;YACL,QAAQ,EAAE,gBAAgB;YAC1B,KAAK,EAAE,SAAI,EAAE;YACb,KAAK,EAAE,YAAY,CAAC,UAAU;YAC9B,cAAc,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,kBAAkB,2BAAU,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;SACzF,CAAC;QAEF,oDAAoD;QACpD,cAAc,CAAC,iBAAiB,GAAG,EAAG,CAAC;QACvC,IAAI,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,iCAAiC,CAAC,KAAK,IAAI,EAAE;YAClE,cAAc,CAAC,iBAAiB,CAAC,eAAe,CAAC,GAAG,IAAI,CAAC;SAC5D;QAED,MAAM,eAAe,GAAG,IAAI,2BAA2B,CACnD,MAAM,EACN,UAAU,EACV,eAAe,EACf,eAAe,EACf,kBAAkB,CAAC,CAAC;QAExB,IAAI;YACA,MAAM,eAAe,CAAC,UAAU,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;YAC5D,MAAM,YAAY,CAAC,qBAAqB,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;SACrE;QAAC,OAAO,WAAW,EAAE;YAClB,IAAI,WAAW,KAAK,IAAI,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE;gBACzD,2EAA2E;gBAC3E,mGAAmG;gBACnG,4FAA4F;gBAC5F,6CAA6C;gBAC7C,yEAAyE;gBACzE,sCAAsC;gBACtC,uFAAuF;gBACvF,6FAA6F;gBAC7F,2BAA2B;gBAC3B,8BAA8B;gBAC9B,mGAAmG;gBACnG,oCAAoC;gBACpC,qFAAqF;gBACrF,IAAI,WAAW,CAAC,UAAU,KAAK,GAAG,IAAI,WAAW,CAAC,UAAU,KAAK,GAAG,EAAE;oBAClE,WAAW,CAAC,QAAQ,GAAG,IAAI,CAAC;iBAC/B;aACJ;YACD,MAAM,WAAW,CAAC;SACrB;QAED,OAAO,eAAe,CAAC;IAC3B,CAAC;IAUD;;OAEG;IACO,iBAAiB,CAAC,OAAe,EAAE,KAAW,EAAE,QAAQ,GAAG,IAAI;QACrE,wDAAwD;QACxD,wGAAwG;QACxG,4CAA4C;QAC5C,IAAI,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,IAAI,CAAC,IAAI,QAAO,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,OAAO,CAAA,KAAK,QAAQ,EAAE;YACjF,OAAO,sCAA0B,CAAC,KAAyB,EAAE,OAAO,CAAgB,CAAC;SACxF;aAAM;YACH,OAAO,KAAK,CAAC,iBAAiB,CAAC,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;SAC5D;IACL,CAAC;IAED;;OAEG;IACK,MAAM,CAAC,4BAA4B,CACvC,EAAwB,EACxB,SAAiB,EACjB,GAAW,EACX,GAAW,EACX,kBAA2B,EAC3B,QAAgB,EAChB,UAAkB,EAClB,MAAwB;QAExB,MAAM,uBAAuB,GAAG,eAAe,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAClE,IAAI,uBAAuB,EAAE;YACzB,OAAO,uBAAuB,CAAC;SAClC;QAED,MAAM,KAAK,GAAG,kBAAkB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;QAExE,MAAM,MAAM,GAAG,EAAE,CACb,GAAG,EACH;YACI,SAAS,EAAE,KAAK;YAChB,KAAK;YACL,YAAY,EAAE,KAAK;YACnB,UAAU,EAAE,CAAC,WAAW,CAAC;YACzB,OAAO,EAAE,SAAS;SACrB,CAAC,CAAC;QAEP,OAAO,IAAI,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC5C,CAAC;IAqBD;;;;;OAKG;IACK,UAAU,CAAC,IAAY,EAAE,EAAU;;QACvC,0EAA0E;QAC1E,qBAAM,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,CAAC,qBAAqB,CAAC,CAAC;QAE/C,sCAAsC;QACtC,gFAAgF;QAChF,IAAI,OAAC,IAAI,CAAC,OAAe,CAAC,iBAAiB,0CAAG,eAAe,OAAM,IAAI,EAAE;YACrE,OAAO;SACV;QAED,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,GAAG,IAAI,CAAC,qBAAqB,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QACrE,MAAM,KAAK,GAAG,0BAAW,CAAC,GAAG,EAAE,CAAC;QAEhC,qFAAqF;QACrF,qFAAqF;QACrF,uEAAuE;QACvE,yFAAyF;QACzF,sDAAsD;QACtD,uFAAuF;QACvF,gCAAgC;QAChC,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,EAAE;YAC1B,IAAI,IAAI,GAAG,KAAK,CAAC;YACjB,IAAI,GAAuB,CAAC;YAC5B,KAAK,MAAM,CAAC,YAAY,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE;gBAC1D,IAAI,KAAK,CAAC,KAAK,IAAI,IAAI,IAAI,GAAG,KAAK,SAAS,EAAE;oBAC1C,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC;oBACnB,GAAG,GAAG,YAAY,CAAC;iBACtB;aACJ;YACD,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAI,CAAE,CAAC;YAClD,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC;gBACvB,SAAS,EAAE,eAAe;gBAC1B,KAAK;gBACL,IAAI,EAAE,eAAe,CAAC,IAAI;gBAC1B,EAAE,EAAE,eAAe,CAAC,EAAE;gBACtB,MAAM,EAAE,eAAe,CAAC,EAAE,GAAG,eAAe,CAAC,IAAI;gBACjD,QAAQ,EAAE,0BAAW,CAAC,GAAG,EAAE,GAAG,eAAe,CAAC,KAAK;aACtD,CAAC,CAAC;YACH,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAI,CAAC,CAAC;SAC/B;QACD,IAAI,CAAC,SAAS,CAAC,GAAG,CACd,KAAK,EACL;YACI,KAAK;YACL,IAAI;YACJ,EAAE;SACL,CACJ,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,QAAQ,EAAE;YACvC,KAAK;YACL,IAAI;YACJ,EAAE,EAAE,EAAE,GAAG,CAAC;SACb,CAAC,CAAC;IACP,CAAC;IAEM,KAAK,CAAC,KAAK;;QACd,gFAAgF;QAChF,IAAI,OAAC,IAAI,CAAC,OAAe,CAAC,iBAAiB,0CAAG,iBAAiB,OAAM,IAAI,EAAE;YACvE,6EAA6E;YAC7E,8EAA8E;YAC9E,gFAAgF;YAChF,iCAAiC;YACjC,uFAAuF;YACvF,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,EAAE,SAAS,EAAE,sBAAsB,EAAE,CAAC,CAAC;YAClE,MAAM,IAAI,KAAK,CAAC,4EAA4E,CAAC,CAAC;SACjG;QAED,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,GAAG,IAAI,CAAC,qBAAqB,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QACrE,sFAAsF;QACtF,sFAAsF;QACtF,uDAAuD;QACvD,IAAI,IAAI,CAAC,aAAa,KAAK,SAAS,EAAE;YAClC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAAC,CAAC;YAC7D,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,oEAAoE,CAAC,CAAC;SACnG;QACD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAExD,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;QAC1B,IAAI,CAAC,aAAa,GAAG,IAAI,uBAAQ,EAAe,CAAC;QACjD,OAAO,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC;IACtC,CAAC;IAMS,KAAK,CAAC,UAAU,CAAC,cAAwB,EAAE,OAAe;QAChE,IAAI,IAAI,CAAC,kBAAkB,EAAE;YACzB,sCAAsC;YACtC,IAAI,CAAC,cAAc,GAAG,CAAC,iBAAyB,EAAE,IAAiC,EAAE,EAAE;gBACnF,IAAI,IAAI,CAAC,UAAU,KAAK,iBAAiB,EAAE;oBACvC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;iBACrC;YACL,CAAC,CAAC;YAEF,IAAI,CAAC,kBAAkB,GAAG,CAAC,GAAmB,EAAE,iBAA0B,EAAE,EAAE;gBAC1E,IAAI,iBAAiB,KAAK,SAAS,IAAI,iBAAiB,KAAK,IAAI,CAAC,UAAU,EAAE;oBAC1E,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;iBAChC;YACL,CAAC,CAAC;SACL;QAED,IAAI,CAAC,eAAgB,CAAC,IAAI,CAAC,mBAAmB,EAAE,IAAI,CAAC,uBAAuB,CAAC,CAAC;QAE9E,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,kBAAkB,EAAE,CAAC,MAAuB,EAAE,EAAE;YAC3D,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;YACjC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC9C,0EAA0E;YAC1E,4FAA4F;YAC5F,mGAAmG;YACnG,4GAA4G;YAC5G,IAAI,IAAI,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE;gBAC9E,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACpC,MAAM,MAAM,GAAG;oBACX,SAAS,EAAE,QAAQ;oBACnB,qFAAqF;oBACrF,KAAK,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;oBACpD,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,IAAI,EAAE,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,IAAI;oBAChB,EAAE,EAAE,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,EAAE;oBACZ,QAAQ,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,0BAAW,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK;iBAC5E,CAAC;gBACF,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;oBAC/C,IAAI,CAAC,MAAM,CAAC,oBAAoB,iCACzB,MAAM,KACT,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,cAAc,EACjC,IAAI,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,cAAc,EAClD,MAAM,EAAE,QAAQ,CAAC,MAAM,IACzB,CAAC;oBACH,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;iBAC9C;qBAAM;oBACH,IAAI,CAAC,MAAM,CAAC,oBAAoB,iCACzB,MAAM,KACT,MAAM,EAAE,CAAC,IACX,CAAC;iBACN;aACJ;QACL,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,MAAyB,EAAE,EAAE;YAC/D,IAAI,IAAI,CAAC,YAAY,KAAK,MAAM,CAAC,KAAK,EAAE;gBACpC,MAAM,GAAG,GAAG,MAAM,CAAC,2BAA2B,CAAC;gBAC/C,IAAI,QAAQ,GAAwB,SAAS,CAAC;gBAC9C,IAAI,MAAM,CAAC,2BAA2B,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,KAAK,GAAG,EAAE;oBACzE,QAAQ,MAAM,CAAC,IAAI,EAAE;wBACjB,KAAK,GAAG,CAAC;wBACT,KAAK,GAAG;4BACJ,QAAQ,GAAG,OAAO,CAAC;4BACnB,MAAM;wBACV,KAAK,GAAG;4BACJ,MAAM;wBACV;4BACI,QAAQ,GAAG,OAAO,CAAC;4BACnB,MAAM;qBACb;iBACJ;gBACD,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC;oBAC3B,SAAS,EAAE,aAAa;oBACxB,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,cAAc,EAAE,GAAG;oBACnB,QAAQ;iBACX,CAAC,CAAC;gBACH,IAAI,CAAC,aAAc,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;gBACpC,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;gBAC/B,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC;aACjC;QACL,CAAC,CAAC,CAAC;QAEH,MAAM,KAAK,CAAC,UAAU,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;IACpD,CAAC;IAES,kBAAkB,CAAC,KAAa,EAAE,QAAkC;QAC1E,uGAAuG;QACvG,QAAQ,KAAK,EAAE;YACX,KAAK,IAAI;gBACL,2BAA2B;gBAC3B,KAAK,CAAC,kBAAkB,CAAC,KAAK,EAAE,CAAC,UAAkB,EAAE,IAAiC,EAAE,EAAE;oBACtF,IAAI,CAAC,IAAI,CAAC,kBAAkB,IAAI,IAAI,CAAC,UAAU,KAAK,UAAU,EAAE;wBAC5D,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;qBAC9B;gBACL,CAAC,CAAC,CAAC;gBACH,MAAM;YAEV,KAAK,QAAQ;gBACT,+BAA+B;gBAC/B,KAAK,CAAC,kBAAkB,CAAC,KAAK,EAAE,CAAC,GAAmB,EAAE,UAAmB,EAAE,EAAE;oBACzE,IAAI,CAAC,IAAI,CAAC,kBAAkB,IAAI,CAAC,UAAU,IAAI,UAAU,KAAK,IAAI,CAAC,UAAU,EAAE;wBAC3E,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;qBAC7B;gBACL,CAAC,CAAC,CAAC;gBACH,MAAM;YAEV,KAAK,MAAM;gBACP,sCAAsC;gBACtC,KAAK,CAAC,kBAAkB,CAAC,KAAK,EAAE,CAAC,oBAA4B,EAAE,OAAgB,EAAE,EAAE;oBAC/E,IAAI,oBAAoB,CAAC,MAAM,KAAK,CAAC;wBACjC,oBAAoB,KAAK,IAAI,CAAC,UAAU;wBACxC,CAAC,IAAI,CAAC,UAAU,IAAI,oBAAoB,KAAK,IAAI,CAAC,QAAQ,CAAC,EAAE;wBAC7D,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,oBAAoB,EAAE,OAAO,CAAC,CAAC;qBACpD;gBACL,CAAC,CAAC,CAAC;gBACH,MAAM;YAEV;gBACI,KAAK,CAAC,kBAAkB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;gBAC1C,MAAM;SACb;IACL,CAAC;IAED;;OAEG;IACO,UAAU,CAAC,mBAA4B,EAAE,MAAW;QAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,eAAe,CAAC;QACpC,qBAAM,CAAC,MAAM,KAAK,SAAS,EAAE,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACtE,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;QAEjC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,mBAAmB,EAAE,IAAI,CAAC,uBAAuB,CAAC,CAAC;QAEnE,IAAI,CAAC,mBAAmB,IAAI,IAAI,CAAC,UAAU,EAAE;YACzC,qEAAqE;YACrE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,qBAAqB,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;SAC3E;QAED,MAAM,CAAC,uBAAuB,CAAC,mBAAmB,CAAC,CAAC;QACpD,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;CACJ;AAtZD,kEAsZC","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 { DriverError } from \"@fluidframework/driver-definitions\";\nimport { OdspError } from \"@fluidframework/odsp-driver-definitions\";\nimport { loggerToMonitoringContext, LoggingError } from \"@fluidframework/telemetry-utils\";\nimport {\n IClient,\n IConnect,\n INack,\n ISequencedDocumentMessage,\n ISignalMessage,\n} from \"@fluidframework/protocol-definitions\";\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\nexport interface ISocketEvents extends IEvent {\n (event: \"server_disconnect\", listener: (error: LoggingError & OdspError) => void);\n}\n\nclass SocketReference extends TypedEventEmitter<ISocketEvents> {\n private references: number = 1;\n private delayDeleteTimeout: ReturnType<typeof setTimeout> | undefined;\n private _socket: SocketIOClient.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 && 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 * @param key - socket reference key\n * @param isFatalError - true if the socket reference should be removed immediately due to a fatal error\n */\n public removeSocketIoReference(isFatalError: boolean) {\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 (isFatalError || 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: SocketIOClient.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 // The server always closes the socket after sending this message\n // fully remove the socket reference now\n socket.on(\"server_disconnect\", (socketError: IOdspSocketError) => {\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.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 this.emit(\"server_disconnect\", error);\n this.closeSocket();\n });\n }\n\n private clearTimer() {\n if (this.delayDeleteTimeout !== undefined) {\n clearTimeout(this.delayDeleteTimeout);\n this.delayDeleteTimeout = undefined;\n }\n }\n\n private closeSocket() {\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 SocketReference.socketIoSockets.delete(this.key);\n\n const socket = this._socket;\n this._socket = undefined;\n\n // Delay closing socket, to make sure all users of socket observe the same event that causes\n // this instance to close, and thus properly record reason for closure.\n // All event raising is synchronous, so clients will have a chance to react before socket is\n // closed without any extra data on why it was closed.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n Promise.resolve().then(() => { socket.disconnect(); });\n }\n\n private 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: 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 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\") === true) {\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) {\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): DriverError {\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 if (canRetry && Number.isInteger(error?.code) && typeof error?.message === \"string\") {\n return errorObjectFromSocketError(error as IOdspSocketError, handler) as DriverError;\n } else {\n return super.createErrorObject(handler, error, canRetry);\n }\n }\n\n /**\n * Gets or create a socket io connection for the given key\n */\n private static getOrCreateSocketIoReference(\n io: 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: SocketIOClient.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 // 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 // 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 serverDisconnectHandler = (error: LoggingError & OdspError) => {\n this.disposeCore(true, error);\n };\n\n protected async initialize(connectMessage: IConnect, timeout: number) {\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!.once(\"server_disconnect\", this.serverDisconnectHandler);\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, message: INack[]) => {\n if (clientIdOrDocumentId.length === 0 ||\n clientIdOrDocumentId === this.documentId ||\n (this.hasDetails && clientIdOrDocumentId === this.clientId)) {\n this.emit(\"nack\", clientIdOrDocumentId, message);\n }\n });\n break;\n\n default:\n super.addTrackedListener(event, listener);\n break;\n }\n }\n\n /**\n * Disconnect from the websocket\n */\n protected disconnect(socketProtocolError: boolean, reason: any) {\n const socket = this.socketReference;\n assert(socket !== undefined, 0x0a2 /* \"reentrancy not supported!\" */);\n this.socketReference = undefined;\n\n this.socket.off(\"server_disconnect\", this.serverDisconnectHandler);\n\n if (!socketProtocolError && 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(socketProtocolError);\n this.emit(\"disconnect\", reason);\n }\n}\n"]}
|
package/dist/odspUtils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"odspUtils.d.ts","sourceRoot":"","sources":["../src/odspUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAClH,OAAO,EAAE,YAAY,EAAmB,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"odspUtils.d.ts","sourceRoot":"","sources":["../src/odspUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAClH,OAAO,EAAE,YAAY,EAAmB,MAAM,oCAAoC,CAAC;AASnF,OAAO,EAAE,yBAAyB,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AAOhG,OAAO,EACH,gBAAgB,EAChB,iBAAiB,EAIjB,6BAA6B,EAC7B,cAAc,EACd,YAAY,EACZ,WAAW,EAEX,+BAA+B,EAClC,MAAM,yCAAyC,CAAC;AAGjD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,eAAO,MAAM,iCAAiC,sCAAsC,CAAC;AAErF,4DAA4D;AAC5D,eAAO,MAAM,SAAS,QAAS,MAAM,WAAwB,CAAC;AAE9D,MAAM,WAAW,iBAAiB;IAC9B,YAAY,EAAE,aAAa,CAAC;IAC5B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAChC,GAAG,EAAE,yBAAyB,EAAE,CAAC;IACjC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;CACtC;AAED,MAAM,WAAW,aAAa,CAAC,CAAC;IAC5B,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,gBAAgB,EAAE,oBAAoB,CAAC;IACvC,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAoB,SAAQ,iBAAiB;IAC1D,2DAA2D;IAC3D,aAAa,CAAC,EAAE,GAAG,CAAC;CACvB;AAUD;;;;;GAKG;AACH,wBAAsB,2BAA2B,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC,CAAC,CAAC,cAoBrG;AAED,wBAAsB,WAAW,CAC7B,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,WAAW,GAAG,SAAS,GACrC,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAsDlC;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAC5B,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,WAAW,GAAG,SAAS,GACrC,OAAO,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAWrC;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAAC,CAAC,EAC7C,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,WAAW,GAAG,SAAS,GACrC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CA2B3B;AAED,MAAM,WAAW,YAAY;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;CACnC;AAED,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,YAAY,GAAG,gBAAgB,CAG9E;AAED,eAAO,MAAM,gBAAgB,0GAQnB,CAAC;AAEX,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,aAAa;;;;;EAqBxD;AAED,wBAAgB,8BAA8B,CAC1C,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,gBAAgB,EAC7B,YAAY,EAAE,YAAY,CAAC,6BAA6B,CAAC,EACzD,gBAAgB,EAAE,OAAO,GAC1B,+BAA+B,CAqDjC;AAED,wBAAgB,sBAAsB,CAAC,eAAe,EAAE,gBAAgB,GAAG,WAAW,CAUrF"}
|
package/dist/odspUtils.js
CHANGED
|
@@ -207,7 +207,10 @@ function toInstrumentedOdspTokenFetcher(logger, resolvedUrl, tokenFetcher, throw
|
|
|
207
207
|
}
|
|
208
208
|
return token;
|
|
209
209
|
}, (error) => {
|
|
210
|
-
|
|
210
|
+
// There is an important but unofficial contract here where token providers can set canRetry: true
|
|
211
|
+
// to hook into the driver's retry logic (e.g. the retry loop when initiating a connection)
|
|
212
|
+
const rawCanRetry = error === null || error === void 0 ? void 0 : error.canRetry;
|
|
213
|
+
const tokenError = telemetry_utils_1.wrapError(error, (errorMessage) => new driver_utils_1.NetworkErrorBasic("tokenFetcherFailed", errorMessage, odsp_driver_definitions_1.OdspErrorType.fetchTokenError, typeof rawCanRetry === "boolean" ? rawCanRetry : false /* canRetry */, { method: name, driverVersion: packageVersion_1.pkgVersion }));
|
|
211
214
|
throw tokenError;
|
|
212
215
|
}), { cancel: "generic" });
|
|
213
216
|
};
|
package/dist/odspUtils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"odspUtils.js","sourceRoot":"","sources":["../src/odspUtils.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 { ITelemetryProperties, ITelemetryBaseLogger, ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { IResolvedUrl, DriverErrorType } from \"@fluidframework/driver-definitions\";\nimport { isOnline, OnlineStatus, RetryableError, NonRetryableError } from \"@fluidframework/driver-utils\";\nimport { assert, performance } from \"@fluidframework/common-utils\";\nimport { ISequencedDocumentMessage, ISnapshotTree } from \"@fluidframework/protocol-definitions\";\nimport { ChildLogger, PerformanceEvent, wrapError } from \"@fluidframework/telemetry-utils\";\nimport {\n fetchIncorrectResponse,\n throwOdspNetworkError,\n getSPOAndGraphRequestIdsFromResponse,\n} from \"@fluidframework/odsp-doclib-utils\";\nimport {\n IOdspResolvedUrl,\n TokenFetchOptions,\n OdspErrorType,\n tokenFromResponse,\n isTokenFromCache,\n OdspResourceTokenFetchOptions,\n ShareLinkTypes,\n TokenFetcher,\n ICacheEntry,\n snapshotKey,\n InstrumentedStorageTokenFetcher,\n} from \"@fluidframework/odsp-driver-definitions\";\nimport { fetch } from \"./fetch\";\nimport { pkgVersion } from \"./packageVersion\";\nimport { IOdspSnapshot } from \"./contracts\";\n\nexport const getWithRetryForTokenRefreshRepeat = \"getWithRetryForTokenRefreshRepeat\";\n\n/** Parse the given url and return the origin (host name) */\nexport const getOrigin = (url: string) => new URL(url).origin;\n\nexport interface ISnapshotContents {\n snapshotTree: ISnapshotTree,\n blobs: Map<string, ArrayBuffer>,\n ops: ISequencedDocumentMessage[],\n sequenceNumber: number | undefined,\n}\n\nexport interface IOdspResponse<T> {\n content: T;\n headers: Map<string, string>;\n commonSpoHeaders: ITelemetryProperties;\n duration: number,\n}\n\nexport interface TokenFetchOptionsEx extends TokenFetchOptions {\n /** previous error we hit in getWithRetryForTokenRefresh */\n previousError?: any;\n}\n\nfunction headersToMap(headers: Headers) {\n const newHeaders = new Map<string, string>();\n for (const [key, value] of headers.entries()) {\n newHeaders.set(key, value);\n }\n return newHeaders;\n}\n\n/**\n * This API should be used with pretty much all network calls (fetch, webSocket connection) in order\n * to correctly handle expired tokens. It relies on callback fetching token, and be able to refetch\n * token on failure. Only specific cases get retry call with refresh = true, all other / unknown errors\n * simply propagate to caller\n */\nexport async function getWithRetryForTokenRefresh<T>(get: (options: TokenFetchOptionsEx) => Promise<T>) {\n return get({ refresh: false }).catch(async (e) => {\n const options: TokenFetchOptionsEx = { refresh: true, previousError: e };\n switch (e.errorType) {\n // If the error is 401 or 403 refresh the token and try once more.\n case DriverErrorType.authorizationError:\n return get({ ...options, claims: e.claims, tenantId: e.tenantId });\n\n case DriverErrorType.incorrectServerResponse: // some error on the wire, retry once\n case OdspErrorType.fetchTokenError: // If the token was null, then retry once.\n return get(options);\n\n default:\n // Caller may determine that it wants one retry\n if (e[getWithRetryForTokenRefreshRepeat] === true) {\n return get(options);\n }\n throw e;\n }\n });\n}\n\nexport async function fetchHelper(\n requestInfo: RequestInfo,\n requestInit: RequestInit | undefined,\n): Promise<IOdspResponse<Response>> {\n const start = performance.now();\n\n // Node-fetch and dom have conflicting typing, force them to work by casting for now\n return fetch(requestInfo, requestInit).then(async (fetchResponse) => {\n const response = fetchResponse as any as Response;\n // Let's assume we can retry.\n if (!response) {\n throw new NonRetryableError(\n \"odspFetchErrorNoResponse\",\n \"No response from fetch call\",\n DriverErrorType.incorrectServerResponse);\n }\n if (!response.ok || response.status < 200 || response.status >= 300) {\n throwOdspNetworkError(\n `odspFetchError [${response.status}]`, response.status, response, await response.text());\n }\n\n const headers = headersToMap(response.headers);\n return {\n content: response,\n headers,\n commonSpoHeaders: getSPOAndGraphRequestIdsFromResponse(headers),\n duration: performance.now() - start,\n };\n }, (error) => {\n // While we do not know for sure whether computer is offline, this error is not actionable and\n // is pretty good indicator we are offline. Treating it as offline scenario will make it\n // easier to see other errors in telemetry.\n let online = isOnline();\n const errorText = `${error}`;\n if (errorText === \"TypeError: Failed to fetch\") {\n online = OnlineStatus.Offline;\n }\n // This error is thrown by fetch() when AbortSignal is provided and it gets cancelled\n if (error.name === \"AbortError\") {\n throw new RetryableError(\"fetchAbort\", \"Fetch Timeout (AbortError)\", OdspErrorType.fetchTimeout);\n }\n // TCP/IP timeout\n if (errorText.indexOf(\"ETIMEDOUT\") !== -1) {\n throw new RetryableError(\"fetchETimedout\", \"Fetch Timeout (ETIMEDOUT)\", OdspErrorType.fetchTimeout);\n }\n\n //\n // WARNING: Do not log error object itself or any of its properties!\n // It could container PII, like URI in message itself, or token in properties.\n // It is also non-serializable object due to circular references.\n //\n if (online === OnlineStatus.Offline) {\n throw new RetryableError(\"OdspFetchOffline\", `Offline: ${errorText}`, DriverErrorType.offlineError);\n } else {\n throw new RetryableError(\"OdspFetchError\", `Fetch error: ${errorText}`, DriverErrorType.fetchFailure);\n }\n });\n}\n\n/**\n * A utility function to fetch and parse as JSON with support for retries\n * @param requestInfo - fetch requestInfo, can be a string\n * @param requestInit - fetch requestInit\n */\nexport async function fetchArray(\n requestInfo: RequestInfo,\n requestInit: RequestInit | undefined,\n): Promise<IOdspResponse<ArrayBuffer>> {\n const { content, headers, commonSpoHeaders, duration } = await fetchHelper(requestInfo, requestInit);\n\n const arrayBuffer = await content.arrayBuffer();\n commonSpoHeaders.bodySize = arrayBuffer.byteLength;\n return {\n headers,\n content: arrayBuffer,\n commonSpoHeaders,\n duration,\n };\n}\n\n/**\n * A utility function to fetch and parse as JSON with support for retries\n * @param requestInfo - fetch requestInfo, can be a string\n * @param requestInit - fetch requestInit\n */\nexport async function fetchAndParseAsJSONHelper<T>(\n requestInfo: RequestInfo,\n requestInit: RequestInit | undefined,\n): Promise<IOdspResponse<T>> {\n const { content, headers, commonSpoHeaders, duration } = await fetchHelper(requestInfo, requestInit);\n let text: string | undefined;\n try {\n text = await content.text();\n } catch (e) {\n // JSON.parse() can fail and message would container full request URI, including\n // tokens... It fails for me with \"Unexpected end of JSON input\" quite often - an attempt to download big file\n // (many ops) almost always ends up with this error - I'd guess 1% of op request end up here... It always\n // succeeds on retry.\n // So do not log error object itself.\n throwOdspNetworkError(\n \"errorWhileParsingFetchResponse\",\n fetchIncorrectResponse,\n content, // response\n text,\n );\n }\n\n commonSpoHeaders.bodySize = text.length;\n const res = {\n headers,\n content: JSON.parse(text),\n commonSpoHeaders,\n duration,\n };\n return res;\n}\n\nexport interface INewFileInfo {\n siteUrl: string;\n driveId: string;\n filename: string;\n filePath: string;\n /**\n * application can request creation of a share link along with the creation of a new file\n * by passing in an optional param to specify the kind of sharing link\n * (at the time of adding this comment Sept/2021), odsp only supports csl\n */\n createLinkType?: ShareLinkTypes;\n}\n\nexport function getOdspResolvedUrl(resolvedUrl: IResolvedUrl): IOdspResolvedUrl {\n assert((resolvedUrl as IOdspResolvedUrl).odspResolvedUrl === true, 0x1de /* \"Not an ODSP resolved url\" */);\n return resolvedUrl as IOdspResolvedUrl;\n}\n\nexport const createOdspLogger = (logger?: ITelemetryBaseLogger) =>\n ChildLogger.create(\n logger,\n \"OdspDriver\",\n { all :\n {\n driverVersion: pkgVersion,\n },\n });\n\nexport function evalBlobsAndTrees(snapshot: IOdspSnapshot) {\n let numTrees = 0;\n let numBlobs = 0;\n let encodedBlobsSize = 0;\n let decodedBlobsSize = 0;\n for (const tree of snapshot.trees) {\n for (const treeEntry of tree.entries) {\n if (treeEntry.type === \"blob\") {\n numBlobs++;\n } else if (treeEntry.type === \"tree\") {\n numTrees++;\n }\n }\n }\n if (snapshot.blobs !== undefined) {\n for (const blob of snapshot.blobs) {\n decodedBlobsSize += blob.size;\n encodedBlobsSize += blob.content.length;\n }\n }\n return { numTrees, numBlobs, encodedBlobsSize, decodedBlobsSize };\n}\n\nexport function toInstrumentedOdspTokenFetcher(\n logger: ITelemetryLogger,\n resolvedUrl: IOdspResolvedUrl,\n tokenFetcher: TokenFetcher<OdspResourceTokenFetchOptions>,\n throwOnNullToken: boolean,\n): InstrumentedStorageTokenFetcher {\n return async (options: TokenFetchOptions, name: string, alwaysRecordTokenFetchTelemetry: boolean = false) => {\n // Telemetry note: if options.refresh is true, there is a potential perf issue:\n // Host should optimize and provide non-expired tokens on all critical paths.\n // Exceptions: race conditions around expiration, revoked tokens, host that does not care\n // (fluid-fetcher)\n return PerformanceEvent.timedExecAsync(\n logger,\n {\n eventName: `${name}_GetToken`,\n attempts: options.refresh ? 2 : 1,\n hasClaims: !!options.claims,\n hasTenantId: !!options.tenantId,\n },\n async (event) => tokenFetcher({\n ...options,\n siteUrl: resolvedUrl.siteUrl,\n driveId: resolvedUrl.driveId,\n itemId: resolvedUrl.itemId,\n }).then((tokenResponse) => {\n const token = tokenFromResponse(tokenResponse);\n // This event alone generates so many events that is materially impacts cost of telemetry\n // Thus do not report end event when it comes back quickly.\n // Note that most of the hosts do not report if result is comming from cache or not,\n // so we can't rely on that here. But always record if specified explicitly for cases such as\n // calling trees/latest during load.\n if (alwaysRecordTokenFetchTelemetry || event.duration >= 32) {\n event.end({ fromCache: isTokenFromCache(tokenResponse), isNull: token === null });\n }\n if (token === null && throwOnNullToken) {\n throw new NonRetryableError(\n \"storageTokenIsNull\",\n `Token is null for ${name} call`,\n OdspErrorType.fetchTokenError,\n { method: name });\n }\n return token;\n }, (error) => {\n const tokenError = wrapError(\n error,\n (errorMessage) => new NonRetryableError(\n \"tokenFetcherFailed\",\n errorMessage,\n OdspErrorType.fetchTokenError,\n { method: name }));\n throw tokenError;\n }),\n { cancel: \"generic\" });\n };\n}\n\nexport function createCacheSnapshotKey(odspResolvedUrl: IOdspResolvedUrl): ICacheEntry {\n const cacheEntry: ICacheEntry = {\n type: snapshotKey,\n key: \"\",\n file: {\n resolvedUrl: odspResolvedUrl,\n docId: odspResolvedUrl.hashedDocumentId,\n },\n };\n return cacheEntry;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"odspUtils.js","sourceRoot":"","sources":["../src/odspUtils.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 { ITelemetryProperties, ITelemetryBaseLogger, ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { IResolvedUrl, DriverErrorType } from \"@fluidframework/driver-definitions\";\nimport {\n isOnline,\n OnlineStatus,\n RetryableError,\n NonRetryableError,\n NetworkErrorBasic,\n} from \"@fluidframework/driver-utils\";\nimport { assert, performance } from \"@fluidframework/common-utils\";\nimport { ISequencedDocumentMessage, ISnapshotTree } from \"@fluidframework/protocol-definitions\";\nimport { ChildLogger, PerformanceEvent, wrapError } from \"@fluidframework/telemetry-utils\";\nimport {\n fetchIncorrectResponse,\n throwOdspNetworkError,\n getSPOAndGraphRequestIdsFromResponse,\n} from \"@fluidframework/odsp-doclib-utils\";\nimport {\n IOdspResolvedUrl,\n TokenFetchOptions,\n OdspErrorType,\n tokenFromResponse,\n isTokenFromCache,\n OdspResourceTokenFetchOptions,\n ShareLinkTypes,\n TokenFetcher,\n ICacheEntry,\n snapshotKey,\n InstrumentedStorageTokenFetcher,\n} from \"@fluidframework/odsp-driver-definitions\";\nimport { fetch } from \"./fetch\";\nimport { pkgVersion } from \"./packageVersion\";\nimport { IOdspSnapshot } from \"./contracts\";\n\nexport const getWithRetryForTokenRefreshRepeat = \"getWithRetryForTokenRefreshRepeat\";\n\n/** Parse the given url and return the origin (host name) */\nexport const getOrigin = (url: string) => new URL(url).origin;\n\nexport interface ISnapshotContents {\n snapshotTree: ISnapshotTree,\n blobs: Map<string, ArrayBuffer>,\n ops: ISequencedDocumentMessage[],\n sequenceNumber: number | undefined,\n}\n\nexport interface IOdspResponse<T> {\n content: T;\n headers: Map<string, string>;\n commonSpoHeaders: ITelemetryProperties;\n duration: number,\n}\n\nexport interface TokenFetchOptionsEx extends TokenFetchOptions {\n /** previous error we hit in getWithRetryForTokenRefresh */\n previousError?: any;\n}\n\nfunction headersToMap(headers: Headers) {\n const newHeaders = new Map<string, string>();\n for (const [key, value] of headers.entries()) {\n newHeaders.set(key, value);\n }\n return newHeaders;\n}\n\n/**\n * This API should be used with pretty much all network calls (fetch, webSocket connection) in order\n * to correctly handle expired tokens. It relies on callback fetching token, and be able to refetch\n * token on failure. Only specific cases get retry call with refresh = true, all other / unknown errors\n * simply propagate to caller\n */\nexport async function getWithRetryForTokenRefresh<T>(get: (options: TokenFetchOptionsEx) => Promise<T>) {\n return get({ refresh: false }).catch(async (e) => {\n const options: TokenFetchOptionsEx = { refresh: true, previousError: e };\n switch (e.errorType) {\n // If the error is 401 or 403 refresh the token and try once more.\n case DriverErrorType.authorizationError:\n return get({ ...options, claims: e.claims, tenantId: e.tenantId });\n\n case DriverErrorType.incorrectServerResponse: // some error on the wire, retry once\n case OdspErrorType.fetchTokenError: // If the token was null, then retry once.\n return get(options);\n\n default:\n // Caller may determine that it wants one retry\n if (e[getWithRetryForTokenRefreshRepeat] === true) {\n return get(options);\n }\n throw e;\n }\n });\n}\n\nexport async function fetchHelper(\n requestInfo: RequestInfo,\n requestInit: RequestInit | undefined,\n): Promise<IOdspResponse<Response>> {\n const start = performance.now();\n\n // Node-fetch and dom have conflicting typing, force them to work by casting for now\n return fetch(requestInfo, requestInit).then(async (fetchResponse) => {\n const response = fetchResponse as any as Response;\n // Let's assume we can retry.\n if (!response) {\n throw new NonRetryableError(\n \"odspFetchErrorNoResponse\",\n \"No response from fetch call\",\n DriverErrorType.incorrectServerResponse);\n }\n if (!response.ok || response.status < 200 || response.status >= 300) {\n throwOdspNetworkError(\n `odspFetchError [${response.status}]`, response.status, response, await response.text());\n }\n\n const headers = headersToMap(response.headers);\n return {\n content: response,\n headers,\n commonSpoHeaders: getSPOAndGraphRequestIdsFromResponse(headers),\n duration: performance.now() - start,\n };\n }, (error) => {\n // While we do not know for sure whether computer is offline, this error is not actionable and\n // is pretty good indicator we are offline. Treating it as offline scenario will make it\n // easier to see other errors in telemetry.\n let online = isOnline();\n const errorText = `${error}`;\n if (errorText === \"TypeError: Failed to fetch\") {\n online = OnlineStatus.Offline;\n }\n // This error is thrown by fetch() when AbortSignal is provided and it gets cancelled\n if (error.name === \"AbortError\") {\n throw new RetryableError(\"fetchAbort\", \"Fetch Timeout (AbortError)\", OdspErrorType.fetchTimeout);\n }\n // TCP/IP timeout\n if (errorText.indexOf(\"ETIMEDOUT\") !== -1) {\n throw new RetryableError(\"fetchETimedout\", \"Fetch Timeout (ETIMEDOUT)\", OdspErrorType.fetchTimeout);\n }\n\n //\n // WARNING: Do not log error object itself or any of its properties!\n // It could container PII, like URI in message itself, or token in properties.\n // It is also non-serializable object due to circular references.\n //\n if (online === OnlineStatus.Offline) {\n throw new RetryableError(\"OdspFetchOffline\", `Offline: ${errorText}`, DriverErrorType.offlineError);\n } else {\n throw new RetryableError(\"OdspFetchError\", `Fetch error: ${errorText}`, DriverErrorType.fetchFailure);\n }\n });\n}\n\n/**\n * A utility function to fetch and parse as JSON with support for retries\n * @param requestInfo - fetch requestInfo, can be a string\n * @param requestInit - fetch requestInit\n */\nexport async function fetchArray(\n requestInfo: RequestInfo,\n requestInit: RequestInit | undefined,\n): Promise<IOdspResponse<ArrayBuffer>> {\n const { content, headers, commonSpoHeaders, duration } = await fetchHelper(requestInfo, requestInit);\n\n const arrayBuffer = await content.arrayBuffer();\n commonSpoHeaders.bodySize = arrayBuffer.byteLength;\n return {\n headers,\n content: arrayBuffer,\n commonSpoHeaders,\n duration,\n };\n}\n\n/**\n * A utility function to fetch and parse as JSON with support for retries\n * @param requestInfo - fetch requestInfo, can be a string\n * @param requestInit - fetch requestInit\n */\nexport async function fetchAndParseAsJSONHelper<T>(\n requestInfo: RequestInfo,\n requestInit: RequestInit | undefined,\n): Promise<IOdspResponse<T>> {\n const { content, headers, commonSpoHeaders, duration } = await fetchHelper(requestInfo, requestInit);\n let text: string | undefined;\n try {\n text = await content.text();\n } catch (e) {\n // JSON.parse() can fail and message would container full request URI, including\n // tokens... It fails for me with \"Unexpected end of JSON input\" quite often - an attempt to download big file\n // (many ops) almost always ends up with this error - I'd guess 1% of op request end up here... It always\n // succeeds on retry.\n // So do not log error object itself.\n throwOdspNetworkError(\n \"errorWhileParsingFetchResponse\",\n fetchIncorrectResponse,\n content, // response\n text,\n );\n }\n\n commonSpoHeaders.bodySize = text.length;\n const res = {\n headers,\n content: JSON.parse(text),\n commonSpoHeaders,\n duration,\n };\n return res;\n}\n\nexport interface INewFileInfo {\n siteUrl: string;\n driveId: string;\n filename: string;\n filePath: string;\n /**\n * application can request creation of a share link along with the creation of a new file\n * by passing in an optional param to specify the kind of sharing link\n * (at the time of adding this comment Sept/2021), odsp only supports csl\n */\n createLinkType?: ShareLinkTypes;\n}\n\nexport function getOdspResolvedUrl(resolvedUrl: IResolvedUrl): IOdspResolvedUrl {\n assert((resolvedUrl as IOdspResolvedUrl).odspResolvedUrl === true, 0x1de /* \"Not an ODSP resolved url\" */);\n return resolvedUrl as IOdspResolvedUrl;\n}\n\nexport const createOdspLogger = (logger?: ITelemetryBaseLogger) =>\n ChildLogger.create(\n logger,\n \"OdspDriver\",\n { all :\n {\n driverVersion: pkgVersion,\n },\n });\n\nexport function evalBlobsAndTrees(snapshot: IOdspSnapshot) {\n let numTrees = 0;\n let numBlobs = 0;\n let encodedBlobsSize = 0;\n let decodedBlobsSize = 0;\n for (const tree of snapshot.trees) {\n for (const treeEntry of tree.entries) {\n if (treeEntry.type === \"blob\") {\n numBlobs++;\n } else if (treeEntry.type === \"tree\") {\n numTrees++;\n }\n }\n }\n if (snapshot.blobs !== undefined) {\n for (const blob of snapshot.blobs) {\n decodedBlobsSize += blob.size;\n encodedBlobsSize += blob.content.length;\n }\n }\n return { numTrees, numBlobs, encodedBlobsSize, decodedBlobsSize };\n}\n\nexport function toInstrumentedOdspTokenFetcher(\n logger: ITelemetryLogger,\n resolvedUrl: IOdspResolvedUrl,\n tokenFetcher: TokenFetcher<OdspResourceTokenFetchOptions>,\n throwOnNullToken: boolean,\n): InstrumentedStorageTokenFetcher {\n return async (options: TokenFetchOptions, name: string, alwaysRecordTokenFetchTelemetry: boolean = false) => {\n // Telemetry note: if options.refresh is true, there is a potential perf issue:\n // Host should optimize and provide non-expired tokens on all critical paths.\n // Exceptions: race conditions around expiration, revoked tokens, host that does not care\n // (fluid-fetcher)\n return PerformanceEvent.timedExecAsync(\n logger,\n {\n eventName: `${name}_GetToken`,\n attempts: options.refresh ? 2 : 1,\n hasClaims: !!options.claims,\n hasTenantId: !!options.tenantId,\n },\n async (event) => tokenFetcher({\n ...options,\n siteUrl: resolvedUrl.siteUrl,\n driveId: resolvedUrl.driveId,\n itemId: resolvedUrl.itemId,\n }).then((tokenResponse) => {\n const token = tokenFromResponse(tokenResponse);\n // This event alone generates so many events that is materially impacts cost of telemetry\n // Thus do not report end event when it comes back quickly.\n // Note that most of the hosts do not report if result is comming from cache or not,\n // so we can't rely on that here. But always record if specified explicitly for cases such as\n // calling trees/latest during load.\n if (alwaysRecordTokenFetchTelemetry || event.duration >= 32) {\n event.end({ fromCache: isTokenFromCache(tokenResponse), isNull: token === null });\n }\n if (token === null && throwOnNullToken) {\n throw new NonRetryableError(\n \"storageTokenIsNull\",\n `Token is null for ${name} call`,\n OdspErrorType.fetchTokenError,\n { method: name });\n }\n return token;\n }, (error) => {\n // There is an important but unofficial contract here where token providers can set canRetry: true\n // to hook into the driver's retry logic (e.g. the retry loop when initiating a connection)\n const rawCanRetry = error?.canRetry;\n const tokenError = wrapError(\n error,\n (errorMessage) => new NetworkErrorBasic(\n \"tokenFetcherFailed\",\n errorMessage,\n OdspErrorType.fetchTokenError,\n typeof rawCanRetry === \"boolean\" ? rawCanRetry : false /* canRetry */,\n { method: name, driverVersion: pkgVersion }));\n throw tokenError;\n }),\n { cancel: \"generic\" });\n };\n}\n\nexport function createCacheSnapshotKey(odspResolvedUrl: IOdspResolvedUrl): ICacheEntry {\n const cacheEntry: ICacheEntry = {\n type: snapshotKey,\n key: \"\",\n file: {\n resolvedUrl: odspResolvedUrl,\n docId: odspResolvedUrl.hashedDocumentId,\n },\n };\n return cacheEntry;\n}\n"]}
|
package/dist/packageVersion.d.ts
CHANGED
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
|
|
6
6
|
*/
|
|
7
7
|
export declare const pkgName = "@fluidframework/odsp-driver";
|
|
8
|
-
export declare const pkgVersion = "0.54.
|
|
8
|
+
export declare const pkgVersion = "0.54.3";
|
|
9
9
|
//# sourceMappingURL=packageVersion.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packageVersion.d.ts","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,OAAO,gCAAgC,CAAC;AACrD,eAAO,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"packageVersion.d.ts","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,OAAO,gCAAgC,CAAC;AACrD,eAAO,MAAM,UAAU,WAAW,CAAC"}
|
package/dist/packageVersion.js
CHANGED
|
@@ -8,5 +8,5 @@
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.pkgVersion = exports.pkgName = void 0;
|
|
10
10
|
exports.pkgName = "@fluidframework/odsp-driver";
|
|
11
|
-
exports.pkgVersion = "0.54.
|
|
11
|
+
exports.pkgVersion = "0.54.3";
|
|
12
12
|
//# sourceMappingURL=packageVersion.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,6BAA6B,CAAC;AACxC,QAAA,UAAU,GAAG,
|
|
1
|
+
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,6BAA6B,CAAC;AACxC,QAAA,UAAU,GAAG,QAAQ,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/odsp-driver\";\nexport const pkgVersion = \"0.54.3\";\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"odspDocumentDeltaConnection.d.ts","sourceRoot":"","sources":["../src/odspDocumentDeltaConnection.ts"],"names":[],"mappings":"AAAA;;;GAGG;;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,oCAAoC,CAAC;AAE9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,
|
|
1
|
+
{"version":3,"file":"odspDocumentDeltaConnection.d.ts","sourceRoot":"","sources":["../src/odspDocumentDeltaConnection.ts"],"names":[],"mappings":"AAAA;;;GAGG;;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,oCAAoC,CAAC;AAE9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAA6B,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAC1F,OAAO,EACH,OAAO,EACP,QAAQ,EAIX,MAAM,sCAAsC,CAAC;AAG9C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAQ9C,MAAM,WAAW,WAAW;IACxB,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAMD,MAAM,WAAW,aAAc,SAAQ,MAAM;IACzC,CAAC,KAAK,EAAE,mBAAmB,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,KAAK,IAAI,OAAE;CACrF;AAwID;;GAEG;AACH,qBAAa,2BAA4B,SAAQ,uBAAuB;IAoKhE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC;IAnKxC;;;;;;;;;;;;;;;OAeG;WACiB,MAAM,CACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,EAAE,EAAE,oBAAoB,EACxB,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;IAsEvF,OAAO,CAAC,eAAe,CAA8B;IAErD,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAS;IAC/C,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAuE;IACjG,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,WAAW;IAWvF;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,4BAA4B;IA8B3C;;;;;;OAMG;IACH,OAAO;IAYP;;;;;OAKG;IACK,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM;IAwD9B,KAAK,IAAI,OAAO,CAAC,WAAW,CAAC;IA4B1C,SAAS,CAAC,uBAAuB,UAAW,YAAY,GAAG,SAAS,UAElE;cAEc,UAAU,CAAC,cAAc,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM;IAqFpE,SAAS,CAAC,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI;IAsC9E;;OAEG;IACH,SAAS,CAAC,UAAU,CAAC,mBAAmB,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG;CAejE"}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { assert, performance, Deferred, TypedEventEmitter } from "@fluidframework/common-utils";
|
|
6
6
|
import { DocumentDeltaConnection } from "@fluidframework/driver-base";
|
|
7
|
+
import { loggerToMonitoringContext } from "@fluidframework/telemetry-utils";
|
|
7
8
|
import { v4 as uuid } from "uuid";
|
|
8
9
|
import { errorObjectFromSocketError } from "./odspError";
|
|
9
10
|
import { pkgVersion } from "./packageVersion";
|
|
@@ -165,6 +166,7 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection {
|
|
|
165
166
|
* @param socketReferenceKeyPrefix - (optional) prefix to isolate socket reuse cache
|
|
166
167
|
*/
|
|
167
168
|
static async create(tenantId, documentId, token, io, client, url, telemetryLogger, timeoutMs, epochTracker, socketReferenceKeyPrefix) {
|
|
169
|
+
const mc = loggerToMonitoringContext(telemetryLogger);
|
|
168
170
|
// enable multiplexing when the websocket url does not include the tenant/document id
|
|
169
171
|
const parsedUrl = new URL(url);
|
|
170
172
|
const enableMultiplexing = !parsedUrl.searchParams.has("documentId") && !parsedUrl.searchParams.has("tenantId");
|
|
@@ -186,8 +188,10 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection {
|
|
|
186
188
|
relayUserAgent: [client.details.environment, ` driverVersion:${pkgVersion}`].join(";"),
|
|
187
189
|
};
|
|
188
190
|
// Reference to this client supporting get_ops flow.
|
|
189
|
-
|
|
190
|
-
|
|
191
|
+
connectMessage.supportedFeatures = {};
|
|
192
|
+
if (mc.config.getBoolean("Fluid.Driver.Odsp.GetOpsEnabled") === true) {
|
|
193
|
+
connectMessage.supportedFeatures[feature_get_ops] = true;
|
|
194
|
+
}
|
|
191
195
|
const deltaConnection = new OdspDocumentDeltaConnection(socket, documentId, socketReference, telemetryLogger, enableMultiplexing);
|
|
192
196
|
try {
|
|
193
197
|
await deltaConnection.initialize(connectMessage, timeoutMs);
|
|
@@ -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 { DriverError } from \"@fluidframework/driver-definitions\";\nimport { OdspError } from \"@fluidframework/odsp-driver-definitions\";\nimport { LoggingError } from \"@fluidframework/telemetry-utils\";\nimport {\n IClient,\n IConnect,\n INack,\n ISequencedDocumentMessage,\n ISignalMessage,\n} from \"@fluidframework/protocol-definitions\";\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\nexport interface ISocketEvents extends IEvent {\n (event: \"server_disconnect\", listener: (error: LoggingError & OdspError) => void);\n}\n\nclass SocketReference extends TypedEventEmitter<ISocketEvents> {\n private references: number = 1;\n private delayDeleteTimeout: ReturnType<typeof setTimeout> | undefined;\n private _socket: SocketIOClient.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 && 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 * @param key - socket reference key\n * @param isFatalError - true if the socket reference should be removed immediately due to a fatal error\n */\n public removeSocketIoReference(isFatalError: boolean) {\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 (isFatalError || 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: SocketIOClient.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 // The server always closes the socket after sending this message\n // fully remove the socket reference now\n socket.on(\"server_disconnect\", (socketError: IOdspSocketError) => {\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.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 this.emit(\"server_disconnect\", error);\n this.closeSocket();\n });\n }\n\n private clearTimer() {\n if (this.delayDeleteTimeout !== undefined) {\n clearTimeout(this.delayDeleteTimeout);\n this.delayDeleteTimeout = undefined;\n }\n }\n\n private closeSocket() {\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 SocketReference.socketIoSockets.delete(this.key);\n\n const socket = this._socket;\n this._socket = undefined;\n\n // Delay closing socket, to make sure all users of socket observe the same event that causes\n // this instance to close, and thus properly record reason for closure.\n // All event raising is synchronous, so clients will have a chance to react before socket is\n // closed without any extra data on why it was closed.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n Promise.resolve().then(() => { socket.disconnect(); });\n }\n\n private 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: SocketIOClientStatic,\n client: IClient,\n url: string,\n telemetryLogger: ITelemetryLogger,\n timeoutMs: number,\n epochTracker: EpochTracker,\n socketReferenceKeyPrefix: string | undefined): Promise<OdspDocumentDeltaConnection> {\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 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 // back-compat: remove cast to any once new definition of IConnect comes through.\n (connectMessage as any).supportedFeatures = { [feature_get_ops]: true };\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) {\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): DriverError {\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 if (canRetry && Number.isInteger(error?.code) && typeof error?.message === \"string\") {\n return errorObjectFromSocketError(error as IOdspSocketError, handler) as DriverError;\n } else {\n return super.createErrorObject(handler, error, canRetry);\n }\n }\n\n /**\n * Gets or create a socket io connection for the given key\n */\n private static getOrCreateSocketIoReference(\n io: 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: SocketIOClient.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 // 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 // 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 serverDisconnectHandler = (error: LoggingError & OdspError) => {\n this.disposeCore(true, error);\n };\n\n protected async initialize(connectMessage: IConnect, timeout: number) {\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!.once(\"server_disconnect\", this.serverDisconnectHandler);\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, message: INack[]) => {\n if (clientIdOrDocumentId.length === 0 ||\n clientIdOrDocumentId === this.documentId ||\n (this.hasDetails && clientIdOrDocumentId === this.clientId)) {\n this.emit(\"nack\", clientIdOrDocumentId, message);\n }\n });\n break;\n\n default:\n super.addTrackedListener(event, listener);\n break;\n }\n }\n\n /**\n * Disconnect from the websocket\n */\n protected disconnect(socketProtocolError: boolean, reason: any) {\n const socket = this.socketReference;\n assert(socket !== undefined, 0x0a2 /* \"reentrancy not supported!\" */);\n this.socketReference = undefined;\n\n this.socket.off(\"server_disconnect\", this.serverDisconnectHandler);\n\n if (!socketProtocolError && 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(socketProtocolError);\n this.emit(\"disconnect\", reason);\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 { DriverError } from \"@fluidframework/driver-definitions\";\nimport { OdspError } from \"@fluidframework/odsp-driver-definitions\";\nimport { loggerToMonitoringContext, LoggingError } from \"@fluidframework/telemetry-utils\";\nimport {\n IClient,\n IConnect,\n INack,\n ISequencedDocumentMessage,\n ISignalMessage,\n} from \"@fluidframework/protocol-definitions\";\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\nexport interface ISocketEvents extends IEvent {\n (event: \"server_disconnect\", listener: (error: LoggingError & OdspError) => void);\n}\n\nclass SocketReference extends TypedEventEmitter<ISocketEvents> {\n private references: number = 1;\n private delayDeleteTimeout: ReturnType<typeof setTimeout> | undefined;\n private _socket: SocketIOClient.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 && 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 * @param key - socket reference key\n * @param isFatalError - true if the socket reference should be removed immediately due to a fatal error\n */\n public removeSocketIoReference(isFatalError: boolean) {\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 (isFatalError || 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: SocketIOClient.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 // The server always closes the socket after sending this message\n // fully remove the socket reference now\n socket.on(\"server_disconnect\", (socketError: IOdspSocketError) => {\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.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 this.emit(\"server_disconnect\", error);\n this.closeSocket();\n });\n }\n\n private clearTimer() {\n if (this.delayDeleteTimeout !== undefined) {\n clearTimeout(this.delayDeleteTimeout);\n this.delayDeleteTimeout = undefined;\n }\n }\n\n private closeSocket() {\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 SocketReference.socketIoSockets.delete(this.key);\n\n const socket = this._socket;\n this._socket = undefined;\n\n // Delay closing socket, to make sure all users of socket observe the same event that causes\n // this instance to close, and thus properly record reason for closure.\n // All event raising is synchronous, so clients will have a chance to react before socket is\n // closed without any extra data on why it was closed.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n Promise.resolve().then(() => { socket.disconnect(); });\n }\n\n private 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: 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 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\") === true) {\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) {\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): DriverError {\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 if (canRetry && Number.isInteger(error?.code) && typeof error?.message === \"string\") {\n return errorObjectFromSocketError(error as IOdspSocketError, handler) as DriverError;\n } else {\n return super.createErrorObject(handler, error, canRetry);\n }\n }\n\n /**\n * Gets or create a socket io connection for the given key\n */\n private static getOrCreateSocketIoReference(\n io: 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: SocketIOClient.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 // 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 // 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 serverDisconnectHandler = (error: LoggingError & OdspError) => {\n this.disposeCore(true, error);\n };\n\n protected async initialize(connectMessage: IConnect, timeout: number) {\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!.once(\"server_disconnect\", this.serverDisconnectHandler);\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, message: INack[]) => {\n if (clientIdOrDocumentId.length === 0 ||\n clientIdOrDocumentId === this.documentId ||\n (this.hasDetails && clientIdOrDocumentId === this.clientId)) {\n this.emit(\"nack\", clientIdOrDocumentId, message);\n }\n });\n break;\n\n default:\n super.addTrackedListener(event, listener);\n break;\n }\n }\n\n /**\n * Disconnect from the websocket\n */\n protected disconnect(socketProtocolError: boolean, reason: any) {\n const socket = this.socketReference;\n assert(socket !== undefined, 0x0a2 /* \"reentrancy not supported!\" */);\n this.socketReference = undefined;\n\n this.socket.off(\"server_disconnect\", this.serverDisconnectHandler);\n\n if (!socketProtocolError && 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(socketProtocolError);\n this.emit(\"disconnect\", reason);\n }\n}\n"]}
|
package/lib/odspUtils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"odspUtils.d.ts","sourceRoot":"","sources":["../src/odspUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAClH,OAAO,EAAE,YAAY,EAAmB,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"odspUtils.d.ts","sourceRoot":"","sources":["../src/odspUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAClH,OAAO,EAAE,YAAY,EAAmB,MAAM,oCAAoC,CAAC;AASnF,OAAO,EAAE,yBAAyB,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AAOhG,OAAO,EACH,gBAAgB,EAChB,iBAAiB,EAIjB,6BAA6B,EAC7B,cAAc,EACd,YAAY,EACZ,WAAW,EAEX,+BAA+B,EAClC,MAAM,yCAAyC,CAAC;AAGjD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,eAAO,MAAM,iCAAiC,sCAAsC,CAAC;AAErF,4DAA4D;AAC5D,eAAO,MAAM,SAAS,QAAS,MAAM,WAAwB,CAAC;AAE9D,MAAM,WAAW,iBAAiB;IAC9B,YAAY,EAAE,aAAa,CAAC;IAC5B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAChC,GAAG,EAAE,yBAAyB,EAAE,CAAC;IACjC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;CACtC;AAED,MAAM,WAAW,aAAa,CAAC,CAAC;IAC5B,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,gBAAgB,EAAE,oBAAoB,CAAC;IACvC,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAoB,SAAQ,iBAAiB;IAC1D,2DAA2D;IAC3D,aAAa,CAAC,EAAE,GAAG,CAAC;CACvB;AAUD;;;;;GAKG;AACH,wBAAsB,2BAA2B,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC,CAAC,CAAC,cAoBrG;AAED,wBAAsB,WAAW,CAC7B,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,WAAW,GAAG,SAAS,GACrC,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAsDlC;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAC5B,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,WAAW,GAAG,SAAS,GACrC,OAAO,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAWrC;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAAC,CAAC,EAC7C,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,WAAW,GAAG,SAAS,GACrC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CA2B3B;AAED,MAAM,WAAW,YAAY;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;CACnC;AAED,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,YAAY,GAAG,gBAAgB,CAG9E;AAED,eAAO,MAAM,gBAAgB,0GAQnB,CAAC;AAEX,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,aAAa;;;;;EAqBxD;AAED,wBAAgB,8BAA8B,CAC1C,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,gBAAgB,EAC7B,YAAY,EAAE,YAAY,CAAC,6BAA6B,CAAC,EACzD,gBAAgB,EAAE,OAAO,GAC1B,+BAA+B,CAqDjC;AAED,wBAAgB,sBAAsB,CAAC,eAAe,EAAE,gBAAgB,GAAG,WAAW,CAUrF"}
|
package/lib/odspUtils.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
import { DriverErrorType } from "@fluidframework/driver-definitions";
|
|
6
|
-
import { isOnline, OnlineStatus, RetryableError, NonRetryableError } from "@fluidframework/driver-utils";
|
|
6
|
+
import { isOnline, OnlineStatus, RetryableError, NonRetryableError, NetworkErrorBasic, } from "@fluidframework/driver-utils";
|
|
7
7
|
import { assert, performance } from "@fluidframework/common-utils";
|
|
8
8
|
import { ChildLogger, PerformanceEvent, wrapError } from "@fluidframework/telemetry-utils";
|
|
9
9
|
import { fetchIncorrectResponse, throwOdspNetworkError, getSPOAndGraphRequestIdsFromResponse, } from "@fluidframework/odsp-doclib-utils";
|
|
@@ -196,7 +196,10 @@ export function toInstrumentedOdspTokenFetcher(logger, resolvedUrl, tokenFetcher
|
|
|
196
196
|
}
|
|
197
197
|
return token;
|
|
198
198
|
}, (error) => {
|
|
199
|
-
|
|
199
|
+
// There is an important but unofficial contract here where token providers can set canRetry: true
|
|
200
|
+
// to hook into the driver's retry logic (e.g. the retry loop when initiating a connection)
|
|
201
|
+
const rawCanRetry = error === null || error === void 0 ? void 0 : error.canRetry;
|
|
202
|
+
const tokenError = wrapError(error, (errorMessage) => new NetworkErrorBasic("tokenFetcherFailed", errorMessage, OdspErrorType.fetchTokenError, typeof rawCanRetry === "boolean" ? rawCanRetry : false /* canRetry */, { method: name, driverVersion: pkgVersion }));
|
|
200
203
|
throw tokenError;
|
|
201
204
|
}), { cancel: "generic" });
|
|
202
205
|
};
|
package/lib/odspUtils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"odspUtils.js","sourceRoot":"","sources":["../src/odspUtils.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 { ITelemetryProperties, ITelemetryBaseLogger, ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { IResolvedUrl, DriverErrorType } from \"@fluidframework/driver-definitions\";\nimport { isOnline, OnlineStatus, RetryableError, NonRetryableError } from \"@fluidframework/driver-utils\";\nimport { assert, performance } from \"@fluidframework/common-utils\";\nimport { ISequencedDocumentMessage, ISnapshotTree } from \"@fluidframework/protocol-definitions\";\nimport { ChildLogger, PerformanceEvent, wrapError } from \"@fluidframework/telemetry-utils\";\nimport {\n fetchIncorrectResponse,\n throwOdspNetworkError,\n getSPOAndGraphRequestIdsFromResponse,\n} from \"@fluidframework/odsp-doclib-utils\";\nimport {\n IOdspResolvedUrl,\n TokenFetchOptions,\n OdspErrorType,\n tokenFromResponse,\n isTokenFromCache,\n OdspResourceTokenFetchOptions,\n ShareLinkTypes,\n TokenFetcher,\n ICacheEntry,\n snapshotKey,\n InstrumentedStorageTokenFetcher,\n} from \"@fluidframework/odsp-driver-definitions\";\nimport { fetch } from \"./fetch\";\nimport { pkgVersion } from \"./packageVersion\";\nimport { IOdspSnapshot } from \"./contracts\";\n\nexport const getWithRetryForTokenRefreshRepeat = \"getWithRetryForTokenRefreshRepeat\";\n\n/** Parse the given url and return the origin (host name) */\nexport const getOrigin = (url: string) => new URL(url).origin;\n\nexport interface ISnapshotContents {\n snapshotTree: ISnapshotTree,\n blobs: Map<string, ArrayBuffer>,\n ops: ISequencedDocumentMessage[],\n sequenceNumber: number | undefined,\n}\n\nexport interface IOdspResponse<T> {\n content: T;\n headers: Map<string, string>;\n commonSpoHeaders: ITelemetryProperties;\n duration: number,\n}\n\nexport interface TokenFetchOptionsEx extends TokenFetchOptions {\n /** previous error we hit in getWithRetryForTokenRefresh */\n previousError?: any;\n}\n\nfunction headersToMap(headers: Headers) {\n const newHeaders = new Map<string, string>();\n for (const [key, value] of headers.entries()) {\n newHeaders.set(key, value);\n }\n return newHeaders;\n}\n\n/**\n * This API should be used with pretty much all network calls (fetch, webSocket connection) in order\n * to correctly handle expired tokens. It relies on callback fetching token, and be able to refetch\n * token on failure. Only specific cases get retry call with refresh = true, all other / unknown errors\n * simply propagate to caller\n */\nexport async function getWithRetryForTokenRefresh<T>(get: (options: TokenFetchOptionsEx) => Promise<T>) {\n return get({ refresh: false }).catch(async (e) => {\n const options: TokenFetchOptionsEx = { refresh: true, previousError: e };\n switch (e.errorType) {\n // If the error is 401 or 403 refresh the token and try once more.\n case DriverErrorType.authorizationError:\n return get({ ...options, claims: e.claims, tenantId: e.tenantId });\n\n case DriverErrorType.incorrectServerResponse: // some error on the wire, retry once\n case OdspErrorType.fetchTokenError: // If the token was null, then retry once.\n return get(options);\n\n default:\n // Caller may determine that it wants one retry\n if (e[getWithRetryForTokenRefreshRepeat] === true) {\n return get(options);\n }\n throw e;\n }\n });\n}\n\nexport async function fetchHelper(\n requestInfo: RequestInfo,\n requestInit: RequestInit | undefined,\n): Promise<IOdspResponse<Response>> {\n const start = performance.now();\n\n // Node-fetch and dom have conflicting typing, force them to work by casting for now\n return fetch(requestInfo, requestInit).then(async (fetchResponse) => {\n const response = fetchResponse as any as Response;\n // Let's assume we can retry.\n if (!response) {\n throw new NonRetryableError(\n \"odspFetchErrorNoResponse\",\n \"No response from fetch call\",\n DriverErrorType.incorrectServerResponse);\n }\n if (!response.ok || response.status < 200 || response.status >= 300) {\n throwOdspNetworkError(\n `odspFetchError [${response.status}]`, response.status, response, await response.text());\n }\n\n const headers = headersToMap(response.headers);\n return {\n content: response,\n headers,\n commonSpoHeaders: getSPOAndGraphRequestIdsFromResponse(headers),\n duration: performance.now() - start,\n };\n }, (error) => {\n // While we do not know for sure whether computer is offline, this error is not actionable and\n // is pretty good indicator we are offline. Treating it as offline scenario will make it\n // easier to see other errors in telemetry.\n let online = isOnline();\n const errorText = `${error}`;\n if (errorText === \"TypeError: Failed to fetch\") {\n online = OnlineStatus.Offline;\n }\n // This error is thrown by fetch() when AbortSignal is provided and it gets cancelled\n if (error.name === \"AbortError\") {\n throw new RetryableError(\"fetchAbort\", \"Fetch Timeout (AbortError)\", OdspErrorType.fetchTimeout);\n }\n // TCP/IP timeout\n if (errorText.indexOf(\"ETIMEDOUT\") !== -1) {\n throw new RetryableError(\"fetchETimedout\", \"Fetch Timeout (ETIMEDOUT)\", OdspErrorType.fetchTimeout);\n }\n\n //\n // WARNING: Do not log error object itself or any of its properties!\n // It could container PII, like URI in message itself, or token in properties.\n // It is also non-serializable object due to circular references.\n //\n if (online === OnlineStatus.Offline) {\n throw new RetryableError(\"OdspFetchOffline\", `Offline: ${errorText}`, DriverErrorType.offlineError);\n } else {\n throw new RetryableError(\"OdspFetchError\", `Fetch error: ${errorText}`, DriverErrorType.fetchFailure);\n }\n });\n}\n\n/**\n * A utility function to fetch and parse as JSON with support for retries\n * @param requestInfo - fetch requestInfo, can be a string\n * @param requestInit - fetch requestInit\n */\nexport async function fetchArray(\n requestInfo: RequestInfo,\n requestInit: RequestInit | undefined,\n): Promise<IOdspResponse<ArrayBuffer>> {\n const { content, headers, commonSpoHeaders, duration } = await fetchHelper(requestInfo, requestInit);\n\n const arrayBuffer = await content.arrayBuffer();\n commonSpoHeaders.bodySize = arrayBuffer.byteLength;\n return {\n headers,\n content: arrayBuffer,\n commonSpoHeaders,\n duration,\n };\n}\n\n/**\n * A utility function to fetch and parse as JSON with support for retries\n * @param requestInfo - fetch requestInfo, can be a string\n * @param requestInit - fetch requestInit\n */\nexport async function fetchAndParseAsJSONHelper<T>(\n requestInfo: RequestInfo,\n requestInit: RequestInit | undefined,\n): Promise<IOdspResponse<T>> {\n const { content, headers, commonSpoHeaders, duration } = await fetchHelper(requestInfo, requestInit);\n let text: string | undefined;\n try {\n text = await content.text();\n } catch (e) {\n // JSON.parse() can fail and message would container full request URI, including\n // tokens... It fails for me with \"Unexpected end of JSON input\" quite often - an attempt to download big file\n // (many ops) almost always ends up with this error - I'd guess 1% of op request end up here... It always\n // succeeds on retry.\n // So do not log error object itself.\n throwOdspNetworkError(\n \"errorWhileParsingFetchResponse\",\n fetchIncorrectResponse,\n content, // response\n text,\n );\n }\n\n commonSpoHeaders.bodySize = text.length;\n const res = {\n headers,\n content: JSON.parse(text),\n commonSpoHeaders,\n duration,\n };\n return res;\n}\n\nexport interface INewFileInfo {\n siteUrl: string;\n driveId: string;\n filename: string;\n filePath: string;\n /**\n * application can request creation of a share link along with the creation of a new file\n * by passing in an optional param to specify the kind of sharing link\n * (at the time of adding this comment Sept/2021), odsp only supports csl\n */\n createLinkType?: ShareLinkTypes;\n}\n\nexport function getOdspResolvedUrl(resolvedUrl: IResolvedUrl): IOdspResolvedUrl {\n assert((resolvedUrl as IOdspResolvedUrl).odspResolvedUrl === true, 0x1de /* \"Not an ODSP resolved url\" */);\n return resolvedUrl as IOdspResolvedUrl;\n}\n\nexport const createOdspLogger = (logger?: ITelemetryBaseLogger) =>\n ChildLogger.create(\n logger,\n \"OdspDriver\",\n { all :\n {\n driverVersion: pkgVersion,\n },\n });\n\nexport function evalBlobsAndTrees(snapshot: IOdspSnapshot) {\n let numTrees = 0;\n let numBlobs = 0;\n let encodedBlobsSize = 0;\n let decodedBlobsSize = 0;\n for (const tree of snapshot.trees) {\n for (const treeEntry of tree.entries) {\n if (treeEntry.type === \"blob\") {\n numBlobs++;\n } else if (treeEntry.type === \"tree\") {\n numTrees++;\n }\n }\n }\n if (snapshot.blobs !== undefined) {\n for (const blob of snapshot.blobs) {\n decodedBlobsSize += blob.size;\n encodedBlobsSize += blob.content.length;\n }\n }\n return { numTrees, numBlobs, encodedBlobsSize, decodedBlobsSize };\n}\n\nexport function toInstrumentedOdspTokenFetcher(\n logger: ITelemetryLogger,\n resolvedUrl: IOdspResolvedUrl,\n tokenFetcher: TokenFetcher<OdspResourceTokenFetchOptions>,\n throwOnNullToken: boolean,\n): InstrumentedStorageTokenFetcher {\n return async (options: TokenFetchOptions, name: string, alwaysRecordTokenFetchTelemetry: boolean = false) => {\n // Telemetry note: if options.refresh is true, there is a potential perf issue:\n // Host should optimize and provide non-expired tokens on all critical paths.\n // Exceptions: race conditions around expiration, revoked tokens, host that does not care\n // (fluid-fetcher)\n return PerformanceEvent.timedExecAsync(\n logger,\n {\n eventName: `${name}_GetToken`,\n attempts: options.refresh ? 2 : 1,\n hasClaims: !!options.claims,\n hasTenantId: !!options.tenantId,\n },\n async (event) => tokenFetcher({\n ...options,\n siteUrl: resolvedUrl.siteUrl,\n driveId: resolvedUrl.driveId,\n itemId: resolvedUrl.itemId,\n }).then((tokenResponse) => {\n const token = tokenFromResponse(tokenResponse);\n // This event alone generates so many events that is materially impacts cost of telemetry\n // Thus do not report end event when it comes back quickly.\n // Note that most of the hosts do not report if result is comming from cache or not,\n // so we can't rely on that here. But always record if specified explicitly for cases such as\n // calling trees/latest during load.\n if (alwaysRecordTokenFetchTelemetry || event.duration >= 32) {\n event.end({ fromCache: isTokenFromCache(tokenResponse), isNull: token === null });\n }\n if (token === null && throwOnNullToken) {\n throw new NonRetryableError(\n \"storageTokenIsNull\",\n `Token is null for ${name} call`,\n OdspErrorType.fetchTokenError,\n { method: name });\n }\n return token;\n }, (error) => {\n const tokenError = wrapError(\n error,\n (errorMessage) => new NonRetryableError(\n \"tokenFetcherFailed\",\n errorMessage,\n OdspErrorType.fetchTokenError,\n { method: name }));\n throw tokenError;\n }),\n { cancel: \"generic\" });\n };\n}\n\nexport function createCacheSnapshotKey(odspResolvedUrl: IOdspResolvedUrl): ICacheEntry {\n const cacheEntry: ICacheEntry = {\n type: snapshotKey,\n key: \"\",\n file: {\n resolvedUrl: odspResolvedUrl,\n docId: odspResolvedUrl.hashedDocumentId,\n },\n };\n return cacheEntry;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"odspUtils.js","sourceRoot":"","sources":["../src/odspUtils.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 { ITelemetryProperties, ITelemetryBaseLogger, ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { IResolvedUrl, DriverErrorType } from \"@fluidframework/driver-definitions\";\nimport {\n isOnline,\n OnlineStatus,\n RetryableError,\n NonRetryableError,\n NetworkErrorBasic,\n} from \"@fluidframework/driver-utils\";\nimport { assert, performance } from \"@fluidframework/common-utils\";\nimport { ISequencedDocumentMessage, ISnapshotTree } from \"@fluidframework/protocol-definitions\";\nimport { ChildLogger, PerformanceEvent, wrapError } from \"@fluidframework/telemetry-utils\";\nimport {\n fetchIncorrectResponse,\n throwOdspNetworkError,\n getSPOAndGraphRequestIdsFromResponse,\n} from \"@fluidframework/odsp-doclib-utils\";\nimport {\n IOdspResolvedUrl,\n TokenFetchOptions,\n OdspErrorType,\n tokenFromResponse,\n isTokenFromCache,\n OdspResourceTokenFetchOptions,\n ShareLinkTypes,\n TokenFetcher,\n ICacheEntry,\n snapshotKey,\n InstrumentedStorageTokenFetcher,\n} from \"@fluidframework/odsp-driver-definitions\";\nimport { fetch } from \"./fetch\";\nimport { pkgVersion } from \"./packageVersion\";\nimport { IOdspSnapshot } from \"./contracts\";\n\nexport const getWithRetryForTokenRefreshRepeat = \"getWithRetryForTokenRefreshRepeat\";\n\n/** Parse the given url and return the origin (host name) */\nexport const getOrigin = (url: string) => new URL(url).origin;\n\nexport interface ISnapshotContents {\n snapshotTree: ISnapshotTree,\n blobs: Map<string, ArrayBuffer>,\n ops: ISequencedDocumentMessage[],\n sequenceNumber: number | undefined,\n}\n\nexport interface IOdspResponse<T> {\n content: T;\n headers: Map<string, string>;\n commonSpoHeaders: ITelemetryProperties;\n duration: number,\n}\n\nexport interface TokenFetchOptionsEx extends TokenFetchOptions {\n /** previous error we hit in getWithRetryForTokenRefresh */\n previousError?: any;\n}\n\nfunction headersToMap(headers: Headers) {\n const newHeaders = new Map<string, string>();\n for (const [key, value] of headers.entries()) {\n newHeaders.set(key, value);\n }\n return newHeaders;\n}\n\n/**\n * This API should be used with pretty much all network calls (fetch, webSocket connection) in order\n * to correctly handle expired tokens. It relies on callback fetching token, and be able to refetch\n * token on failure. Only specific cases get retry call with refresh = true, all other / unknown errors\n * simply propagate to caller\n */\nexport async function getWithRetryForTokenRefresh<T>(get: (options: TokenFetchOptionsEx) => Promise<T>) {\n return get({ refresh: false }).catch(async (e) => {\n const options: TokenFetchOptionsEx = { refresh: true, previousError: e };\n switch (e.errorType) {\n // If the error is 401 or 403 refresh the token and try once more.\n case DriverErrorType.authorizationError:\n return get({ ...options, claims: e.claims, tenantId: e.tenantId });\n\n case DriverErrorType.incorrectServerResponse: // some error on the wire, retry once\n case OdspErrorType.fetchTokenError: // If the token was null, then retry once.\n return get(options);\n\n default:\n // Caller may determine that it wants one retry\n if (e[getWithRetryForTokenRefreshRepeat] === true) {\n return get(options);\n }\n throw e;\n }\n });\n}\n\nexport async function fetchHelper(\n requestInfo: RequestInfo,\n requestInit: RequestInit | undefined,\n): Promise<IOdspResponse<Response>> {\n const start = performance.now();\n\n // Node-fetch and dom have conflicting typing, force them to work by casting for now\n return fetch(requestInfo, requestInit).then(async (fetchResponse) => {\n const response = fetchResponse as any as Response;\n // Let's assume we can retry.\n if (!response) {\n throw new NonRetryableError(\n \"odspFetchErrorNoResponse\",\n \"No response from fetch call\",\n DriverErrorType.incorrectServerResponse);\n }\n if (!response.ok || response.status < 200 || response.status >= 300) {\n throwOdspNetworkError(\n `odspFetchError [${response.status}]`, response.status, response, await response.text());\n }\n\n const headers = headersToMap(response.headers);\n return {\n content: response,\n headers,\n commonSpoHeaders: getSPOAndGraphRequestIdsFromResponse(headers),\n duration: performance.now() - start,\n };\n }, (error) => {\n // While we do not know for sure whether computer is offline, this error is not actionable and\n // is pretty good indicator we are offline. Treating it as offline scenario will make it\n // easier to see other errors in telemetry.\n let online = isOnline();\n const errorText = `${error}`;\n if (errorText === \"TypeError: Failed to fetch\") {\n online = OnlineStatus.Offline;\n }\n // This error is thrown by fetch() when AbortSignal is provided and it gets cancelled\n if (error.name === \"AbortError\") {\n throw new RetryableError(\"fetchAbort\", \"Fetch Timeout (AbortError)\", OdspErrorType.fetchTimeout);\n }\n // TCP/IP timeout\n if (errorText.indexOf(\"ETIMEDOUT\") !== -1) {\n throw new RetryableError(\"fetchETimedout\", \"Fetch Timeout (ETIMEDOUT)\", OdspErrorType.fetchTimeout);\n }\n\n //\n // WARNING: Do not log error object itself or any of its properties!\n // It could container PII, like URI in message itself, or token in properties.\n // It is also non-serializable object due to circular references.\n //\n if (online === OnlineStatus.Offline) {\n throw new RetryableError(\"OdspFetchOffline\", `Offline: ${errorText}`, DriverErrorType.offlineError);\n } else {\n throw new RetryableError(\"OdspFetchError\", `Fetch error: ${errorText}`, DriverErrorType.fetchFailure);\n }\n });\n}\n\n/**\n * A utility function to fetch and parse as JSON with support for retries\n * @param requestInfo - fetch requestInfo, can be a string\n * @param requestInit - fetch requestInit\n */\nexport async function fetchArray(\n requestInfo: RequestInfo,\n requestInit: RequestInit | undefined,\n): Promise<IOdspResponse<ArrayBuffer>> {\n const { content, headers, commonSpoHeaders, duration } = await fetchHelper(requestInfo, requestInit);\n\n const arrayBuffer = await content.arrayBuffer();\n commonSpoHeaders.bodySize = arrayBuffer.byteLength;\n return {\n headers,\n content: arrayBuffer,\n commonSpoHeaders,\n duration,\n };\n}\n\n/**\n * A utility function to fetch and parse as JSON with support for retries\n * @param requestInfo - fetch requestInfo, can be a string\n * @param requestInit - fetch requestInit\n */\nexport async function fetchAndParseAsJSONHelper<T>(\n requestInfo: RequestInfo,\n requestInit: RequestInit | undefined,\n): Promise<IOdspResponse<T>> {\n const { content, headers, commonSpoHeaders, duration } = await fetchHelper(requestInfo, requestInit);\n let text: string | undefined;\n try {\n text = await content.text();\n } catch (e) {\n // JSON.parse() can fail and message would container full request URI, including\n // tokens... It fails for me with \"Unexpected end of JSON input\" quite often - an attempt to download big file\n // (many ops) almost always ends up with this error - I'd guess 1% of op request end up here... It always\n // succeeds on retry.\n // So do not log error object itself.\n throwOdspNetworkError(\n \"errorWhileParsingFetchResponse\",\n fetchIncorrectResponse,\n content, // response\n text,\n );\n }\n\n commonSpoHeaders.bodySize = text.length;\n const res = {\n headers,\n content: JSON.parse(text),\n commonSpoHeaders,\n duration,\n };\n return res;\n}\n\nexport interface INewFileInfo {\n siteUrl: string;\n driveId: string;\n filename: string;\n filePath: string;\n /**\n * application can request creation of a share link along with the creation of a new file\n * by passing in an optional param to specify the kind of sharing link\n * (at the time of adding this comment Sept/2021), odsp only supports csl\n */\n createLinkType?: ShareLinkTypes;\n}\n\nexport function getOdspResolvedUrl(resolvedUrl: IResolvedUrl): IOdspResolvedUrl {\n assert((resolvedUrl as IOdspResolvedUrl).odspResolvedUrl === true, 0x1de /* \"Not an ODSP resolved url\" */);\n return resolvedUrl as IOdspResolvedUrl;\n}\n\nexport const createOdspLogger = (logger?: ITelemetryBaseLogger) =>\n ChildLogger.create(\n logger,\n \"OdspDriver\",\n { all :\n {\n driverVersion: pkgVersion,\n },\n });\n\nexport function evalBlobsAndTrees(snapshot: IOdspSnapshot) {\n let numTrees = 0;\n let numBlobs = 0;\n let encodedBlobsSize = 0;\n let decodedBlobsSize = 0;\n for (const tree of snapshot.trees) {\n for (const treeEntry of tree.entries) {\n if (treeEntry.type === \"blob\") {\n numBlobs++;\n } else if (treeEntry.type === \"tree\") {\n numTrees++;\n }\n }\n }\n if (snapshot.blobs !== undefined) {\n for (const blob of snapshot.blobs) {\n decodedBlobsSize += blob.size;\n encodedBlobsSize += blob.content.length;\n }\n }\n return { numTrees, numBlobs, encodedBlobsSize, decodedBlobsSize };\n}\n\nexport function toInstrumentedOdspTokenFetcher(\n logger: ITelemetryLogger,\n resolvedUrl: IOdspResolvedUrl,\n tokenFetcher: TokenFetcher<OdspResourceTokenFetchOptions>,\n throwOnNullToken: boolean,\n): InstrumentedStorageTokenFetcher {\n return async (options: TokenFetchOptions, name: string, alwaysRecordTokenFetchTelemetry: boolean = false) => {\n // Telemetry note: if options.refresh is true, there is a potential perf issue:\n // Host should optimize and provide non-expired tokens on all critical paths.\n // Exceptions: race conditions around expiration, revoked tokens, host that does not care\n // (fluid-fetcher)\n return PerformanceEvent.timedExecAsync(\n logger,\n {\n eventName: `${name}_GetToken`,\n attempts: options.refresh ? 2 : 1,\n hasClaims: !!options.claims,\n hasTenantId: !!options.tenantId,\n },\n async (event) => tokenFetcher({\n ...options,\n siteUrl: resolvedUrl.siteUrl,\n driveId: resolvedUrl.driveId,\n itemId: resolvedUrl.itemId,\n }).then((tokenResponse) => {\n const token = tokenFromResponse(tokenResponse);\n // This event alone generates so many events that is materially impacts cost of telemetry\n // Thus do not report end event when it comes back quickly.\n // Note that most of the hosts do not report if result is comming from cache or not,\n // so we can't rely on that here. But always record if specified explicitly for cases such as\n // calling trees/latest during load.\n if (alwaysRecordTokenFetchTelemetry || event.duration >= 32) {\n event.end({ fromCache: isTokenFromCache(tokenResponse), isNull: token === null });\n }\n if (token === null && throwOnNullToken) {\n throw new NonRetryableError(\n \"storageTokenIsNull\",\n `Token is null for ${name} call`,\n OdspErrorType.fetchTokenError,\n { method: name });\n }\n return token;\n }, (error) => {\n // There is an important but unofficial contract here where token providers can set canRetry: true\n // to hook into the driver's retry logic (e.g. the retry loop when initiating a connection)\n const rawCanRetry = error?.canRetry;\n const tokenError = wrapError(\n error,\n (errorMessage) => new NetworkErrorBasic(\n \"tokenFetcherFailed\",\n errorMessage,\n OdspErrorType.fetchTokenError,\n typeof rawCanRetry === \"boolean\" ? rawCanRetry : false /* canRetry */,\n { method: name, driverVersion: pkgVersion }));\n throw tokenError;\n }),\n { cancel: \"generic\" });\n };\n}\n\nexport function createCacheSnapshotKey(odspResolvedUrl: IOdspResolvedUrl): ICacheEntry {\n const cacheEntry: ICacheEntry = {\n type: snapshotKey,\n key: \"\",\n file: {\n resolvedUrl: odspResolvedUrl,\n docId: odspResolvedUrl.hashedDocumentId,\n },\n };\n return cacheEntry;\n}\n"]}
|
package/lib/packageVersion.d.ts
CHANGED
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
|
|
6
6
|
*/
|
|
7
7
|
export declare const pkgName = "@fluidframework/odsp-driver";
|
|
8
|
-
export declare const pkgVersion = "0.54.
|
|
8
|
+
export declare const pkgVersion = "0.54.3";
|
|
9
9
|
//# sourceMappingURL=packageVersion.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packageVersion.d.ts","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,OAAO,gCAAgC,CAAC;AACrD,eAAO,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"packageVersion.d.ts","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,OAAO,gCAAgC,CAAC;AACrD,eAAO,MAAM,UAAU,WAAW,CAAC"}
|
package/lib/packageVersion.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,6BAA6B,CAAC;AACrD,MAAM,CAAC,MAAM,UAAU,GAAG,
|
|
1
|
+
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,6BAA6B,CAAC;AACrD,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/odsp-driver\";\nexport const pkgVersion = \"0.54.3\";\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/odsp-driver",
|
|
3
|
-
"version": "0.54.
|
|
3
|
+
"version": "0.54.3",
|
|
4
4
|
"description": "Socket storage implementation for SPO and ODC",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": "https://github.com/microsoft/FluidFramework",
|
|
@@ -58,15 +58,15 @@
|
|
|
58
58
|
"@fluidframework/common-definitions": "^0.20.1",
|
|
59
59
|
"@fluidframework/common-utils": "^0.32.1",
|
|
60
60
|
"@fluidframework/core-interfaces": "^0.41.0",
|
|
61
|
-
"@fluidframework/driver-base": "0.54.
|
|
61
|
+
"@fluidframework/driver-base": "^0.54.3",
|
|
62
62
|
"@fluidframework/driver-definitions": "^0.43.0",
|
|
63
|
-
"@fluidframework/driver-utils": "0.54.
|
|
63
|
+
"@fluidframework/driver-utils": "^0.54.3",
|
|
64
64
|
"@fluidframework/gitresources": "^0.1034.0",
|
|
65
|
-
"@fluidframework/odsp-doclib-utils": "0.54.
|
|
66
|
-
"@fluidframework/odsp-driver-definitions": "0.54.
|
|
65
|
+
"@fluidframework/odsp-doclib-utils": "^0.54.3",
|
|
66
|
+
"@fluidframework/odsp-driver-definitions": "^0.54.3",
|
|
67
67
|
"@fluidframework/protocol-base": "^0.1034.0",
|
|
68
68
|
"@fluidframework/protocol-definitions": "^0.1026.0",
|
|
69
|
-
"@fluidframework/telemetry-utils": "0.54.
|
|
69
|
+
"@fluidframework/telemetry-utils": "^0.54.3",
|
|
70
70
|
"abort-controller": "^3.0.0",
|
|
71
71
|
"node-fetch": "^2.6.1",
|
|
72
72
|
"socket.io-client": "^2.4.0",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@fluidframework/build-common": "^0.23.0",
|
|
77
77
|
"@fluidframework/eslint-config-fluid": "^0.24.0",
|
|
78
|
-
"@fluidframework/mocha-test-setup": "0.54.
|
|
78
|
+
"@fluidframework/mocha-test-setup": "^0.54.3",
|
|
79
79
|
"@microsoft/api-extractor": "^7.16.1",
|
|
80
80
|
"@types/mocha": "^8.2.2",
|
|
81
81
|
"@types/node-fetch": "^2.5.10",
|
|
@@ -8,7 +8,7 @@ import { assert, performance, Deferred, TypedEventEmitter } from "@fluidframewor
|
|
|
8
8
|
import { DocumentDeltaConnection } from "@fluidframework/driver-base";
|
|
9
9
|
import { DriverError } from "@fluidframework/driver-definitions";
|
|
10
10
|
import { OdspError } from "@fluidframework/odsp-driver-definitions";
|
|
11
|
-
import { LoggingError } from "@fluidframework/telemetry-utils";
|
|
11
|
+
import { loggerToMonitoringContext, LoggingError } from "@fluidframework/telemetry-utils";
|
|
12
12
|
import {
|
|
13
13
|
IClient,
|
|
14
14
|
IConnect,
|
|
@@ -204,6 +204,8 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection {
|
|
|
204
204
|
timeoutMs: number,
|
|
205
205
|
epochTracker: EpochTracker,
|
|
206
206
|
socketReferenceKeyPrefix: string | undefined): Promise<OdspDocumentDeltaConnection> {
|
|
207
|
+
const mc = loggerToMonitoringContext(telemetryLogger);
|
|
208
|
+
|
|
207
209
|
// enable multiplexing when the websocket url does not include the tenant/document id
|
|
208
210
|
const parsedUrl = new URL(url);
|
|
209
211
|
const enableMultiplexing = !parsedUrl.searchParams.has("documentId") && !parsedUrl.searchParams.has("tenantId");
|
|
@@ -231,8 +233,10 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection {
|
|
|
231
233
|
};
|
|
232
234
|
|
|
233
235
|
// Reference to this client supporting get_ops flow.
|
|
234
|
-
|
|
235
|
-
(
|
|
236
|
+
connectMessage.supportedFeatures = { };
|
|
237
|
+
if (mc.config.getBoolean("Fluid.Driver.Odsp.GetOpsEnabled") === true) {
|
|
238
|
+
connectMessage.supportedFeatures[feature_get_ops] = true;
|
|
239
|
+
}
|
|
236
240
|
|
|
237
241
|
const deltaConnection = new OdspDocumentDeltaConnection(
|
|
238
242
|
socket,
|
package/src/odspUtils.ts
CHANGED
|
@@ -5,7 +5,13 @@
|
|
|
5
5
|
|
|
6
6
|
import { ITelemetryProperties, ITelemetryBaseLogger, ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
7
7
|
import { IResolvedUrl, DriverErrorType } from "@fluidframework/driver-definitions";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
isOnline,
|
|
10
|
+
OnlineStatus,
|
|
11
|
+
RetryableError,
|
|
12
|
+
NonRetryableError,
|
|
13
|
+
NetworkErrorBasic,
|
|
14
|
+
} from "@fluidframework/driver-utils";
|
|
9
15
|
import { assert, performance } from "@fluidframework/common-utils";
|
|
10
16
|
import { ISequencedDocumentMessage, ISnapshotTree } from "@fluidframework/protocol-definitions";
|
|
11
17
|
import { ChildLogger, PerformanceEvent, wrapError } from "@fluidframework/telemetry-utils";
|
|
@@ -302,13 +308,17 @@ export function toInstrumentedOdspTokenFetcher(
|
|
|
302
308
|
}
|
|
303
309
|
return token;
|
|
304
310
|
}, (error) => {
|
|
311
|
+
// There is an important but unofficial contract here where token providers can set canRetry: true
|
|
312
|
+
// to hook into the driver's retry logic (e.g. the retry loop when initiating a connection)
|
|
313
|
+
const rawCanRetry = error?.canRetry;
|
|
305
314
|
const tokenError = wrapError(
|
|
306
315
|
error,
|
|
307
|
-
(errorMessage) => new
|
|
316
|
+
(errorMessage) => new NetworkErrorBasic(
|
|
308
317
|
"tokenFetcherFailed",
|
|
309
318
|
errorMessage,
|
|
310
319
|
OdspErrorType.fetchTokenError,
|
|
311
|
-
|
|
320
|
+
typeof rawCanRetry === "boolean" ? rawCanRetry : false /* canRetry */,
|
|
321
|
+
{ method: name, driverVersion: pkgVersion }));
|
|
312
322
|
throw tokenError;
|
|
313
323
|
}),
|
|
314
324
|
{ cancel: "generic" });
|
package/src/packageVersion.ts
CHANGED