From 3a9d35046ee384d6cdc22c19bc6ed173f132146d Mon Sep 17 00:00:00 2001 From: Jatin Garg <48029724+jatgarg@users.noreply.github.com> Date: Fri, 5 Feb 2021 15:17:12 -0800 Subject: [PATCH] Handle null tokens in odsp driver. (#5010) --- packages/drivers/odsp-driver/src/odspDocumentService.ts | 8 +++++++- .../odsp-driver/src/odspDocumentServiceFactoryCore.ts | 9 +++++++-- packages/drivers/odsp-driver/src/odspUtils.ts | 3 +++ packages/utils/odsp-doclib-utils/src/odspErrorUtils.ts | 7 +++++++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/packages/drivers/odsp-driver/src/odspDocumentService.ts b/packages/drivers/odsp-driver/src/odspDocumentService.ts index 4883924a8986..f791d3a276e9 100644 --- a/packages/drivers/odsp-driver/src/odspDocumentService.ts +++ b/packages/drivers/odsp-driver/src/odspDocumentService.ts @@ -18,6 +18,7 @@ import { IDocumentStorageService, } from "@fluidframework/driver-definitions"; import { canRetryOnError } from "@fluidframework/driver-utils"; +import { fetchTokenErrorCode } from "@fluidframework/odsp-doclib-utils"; import { IClient, IErrorTrackingService, @@ -38,6 +39,7 @@ import { fetchJoinSession } from "./vroom"; import { isOdcOrigin } from "./odspUrlHelper"; import { TokenFetchOptions } from "./tokenFetch"; import { EpochTracker } from "./epochTracker"; +import { throwOdspNetworkError } from "./odspError"; const afdUrlConnectExpirationMs = 6 * 60 * 60 * 1000; // 6 hours const lastAfdConnectionTimeMsKey = "LastAfdConnectionTimeMs"; @@ -257,12 +259,16 @@ export class OdspDocumentService implements IDocumentService { throw new Error("websocket endpoint should be defined"); } + const finalSocketToken = webSocketToken ?? (websocketEndpoint.socketToken || null); + if (finalSocketToken === null) { + throwOdspNetworkError("Push Token is null", fetchTokenErrorCode); + } try { const connection = await this.connectToDeltaStreamWithRetry( websocketEndpoint.tenantId, websocketEndpoint.id, // Accounts for ODC where websocket token is returned as part of joinsession response payload - webSocketToken ?? (websocketEndpoint.socketToken || null), + finalSocketToken, io, client, websocketEndpoint.deltaStreamSocketUrl, diff --git a/packages/drivers/odsp-driver/src/odspDocumentServiceFactoryCore.ts b/packages/drivers/odsp-driver/src/odspDocumentServiceFactoryCore.ts index 36610db26417..e62b3a251f95 100644 --- a/packages/drivers/odsp-driver/src/odspDocumentServiceFactoryCore.ts +++ b/packages/drivers/odsp-driver/src/odspDocumentServiceFactoryCore.ts @@ -15,6 +15,7 @@ import { PerformanceEvent, } from "@fluidframework/telemetry-utils"; import { ensureFluidResolvedUrl } from "@fluidframework/driver-utils"; +import { fetchTokenErrorCode, throwOdspNetworkError } from "@fluidframework/odsp-doclib-utils"; import { IOdspResolvedUrl, HostStoragePolicy } from "./contracts"; import { LocalPersistentCache, @@ -155,8 +156,12 @@ export class OdspDocumentServiceFactoryCore implements IDocumentServiceFactory { }, async (event) => tokenFetcher(resolvedUrl.siteUrl, options.refresh, options.claims) .then((tokenResponse) => { - event.end({ fromCache: isTokenFromCache(tokenResponse) }); - return tokenFromResponse(tokenResponse); + const token = tokenFromResponse(tokenResponse); + event.end({ fromCache: isTokenFromCache(tokenResponse), isNull: token === null ? true : false }); + if (token === null) { + throwOdspNetworkError("Storage Token is null", fetchTokenErrorCode); + } + return token; })); }; } diff --git a/packages/drivers/odsp-driver/src/odspUtils.ts b/packages/drivers/odsp-driver/src/odspUtils.ts index 21801e36956f..91aab115e467 100644 --- a/packages/drivers/odsp-driver/src/odspUtils.ts +++ b/packages/drivers/odsp-driver/src/odspUtils.ts @@ -10,6 +10,7 @@ import { offlineFetchFailureStatusCode, fetchFailureStatusCode, fetchTimeoutStatusCode, + OdspErrorType, } from "@fluidframework/odsp-doclib-utils"; import { default as fetch, @@ -48,6 +49,8 @@ export async function getWithRetryForTokenRefresh(get: (options: TokenFetchOp return get({ refresh: true, claims: e.claims }); // fetchIncorrectResponse indicates some error on the wire, retry once. case DriverErrorType.incorrectServerResponse: + // If the token was null, then retry once. + case OdspErrorType.fetchTokenError: return get({ refresh: true }); default: // All code paths (deltas, blobs, trees) already throw exceptions. diff --git a/packages/utils/odsp-doclib-utils/src/odspErrorUtils.ts b/packages/utils/odsp-doclib-utils/src/odspErrorUtils.ts index 2d5d7cc05f0f..7da5050e2212 100644 --- a/packages/utils/odsp-doclib-utils/src/odspErrorUtils.ts +++ b/packages/utils/odsp-doclib-utils/src/odspErrorUtils.ts @@ -27,6 +27,8 @@ export const fetchTimeoutStatusCode = 713; // with the server epoch version, the server throws this error code. // This indicates that the file/container has been modified externally. export const fluidEpochMismatchError = 409; +// Error code for when the fetched token is null. +export const fetchTokenErrorCode = 724; export enum OdspErrorType { /** @@ -64,6 +66,8 @@ export enum OdspErrorType { * does not match the one at the server. */ epochVersionMismatch = "epochVersionMismatch", + + fetchTokenError = "fetchTokenError", } /** @@ -135,6 +139,9 @@ export function createOdspNetworkError( case fetchTimeoutStatusCode: error = new NonRetryableError(errorMessage, OdspErrorType.fetchTimeout, statusCode); break; + case fetchTokenErrorCode: + error = new NonRetryableError(errorMessage, OdspErrorType.fetchTokenError, statusCode); + break; default: error = createGenericNetworkError(errorMessage, true, retryAfterSeconds, statusCode); }