From cecbf4f081439f578f7c16745d16033734a2714d Mon Sep 17 00:00:00 2001 From: gnoff Date: Wed, 15 Nov 2023 20:58:39 +0000 Subject: [PATCH] [Fizz] handle errors in `onHeaders` (#27712) `onHeaders` can throw however for now we can assume that headers are optimistic values since the only things we produce for them are preload links. This is a pragmatic decision because React could concievably have headers in the future which were not optimistic and thus non-optional however it is hard to imagine what these headers might be in practice. If we need to change this behavior to be fatal in the future it would be a breaking change. This commit adds error logging when `onHeaders` throws and ensures the request can continue to render successfully. DiffTrain build for [ee68446ff198755bd38202ac9139275b657968b0](https://github.com/facebook/react/commit/ee68446ff198755bd38202ac9139275b657968b0) --- compiled/facebook-www/REVISION | 2 +- compiled/facebook-www/ReactART-dev.modern.js | 2 +- compiled/facebook-www/ReactART-prod.modern.js | 4 +- .../ReactDOMServer-dev.classic.js | 37 ++-- .../facebook-www/ReactDOMServer-dev.modern.js | 37 ++-- .../ReactDOMServer-prod.classic.js | 173 +++++++++--------- .../ReactDOMServer-prod.modern.js | 173 +++++++++--------- .../ReactDOMServerStreaming-dev.modern.js | 29 +-- .../ReactDOMServerStreaming-prod.modern.js | 125 +++++++------ 9 files changed, 298 insertions(+), 284 deletions(-) diff --git a/compiled/facebook-www/REVISION b/compiled/facebook-www/REVISION index 6cc40e1e7242f..16869e8224ceb 100644 --- a/compiled/facebook-www/REVISION +++ b/compiled/facebook-www/REVISION @@ -1 +1 @@ -593ecee66a609d4a4c2b36b39b1e5e23b2456dd1 +ee68446ff198755bd38202ac9139275b657968b0 diff --git a/compiled/facebook-www/ReactART-dev.modern.js b/compiled/facebook-www/ReactART-dev.modern.js index ecdafe82b0bbb..1a0eff96302a5 100644 --- a/compiled/facebook-www/ReactART-dev.modern.js +++ b/compiled/facebook-www/ReactART-dev.modern.js @@ -66,7 +66,7 @@ if (__DEV__) { return self; } - var ReactVersion = "18.3.0-www-modern-7ac3e370"; + var ReactVersion = "18.3.0-www-modern-b8021dcf"; var LegacyRoot = 0; var ConcurrentRoot = 1; diff --git a/compiled/facebook-www/ReactART-prod.modern.js b/compiled/facebook-www/ReactART-prod.modern.js index 0b7f733d433b9..d9aab039c043b 100644 --- a/compiled/facebook-www/ReactART-prod.modern.js +++ b/compiled/facebook-www/ReactART-prod.modern.js @@ -9905,7 +9905,7 @@ var slice = Array.prototype.slice, return null; }, bundleType: 0, - version: "18.3.0-www-modern-fc53dc79", + version: "18.3.0-www-modern-88df5dd9", rendererPackageName: "react-art" }; var internals$jscomp$inline_1302 = { @@ -9936,7 +9936,7 @@ var internals$jscomp$inline_1302 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-www-modern-fc53dc79" + reconcilerVersion: "18.3.0-www-modern-88df5dd9" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_1303 = __REACT_DEVTOOLS_GLOBAL_HOOK__; diff --git a/compiled/facebook-www/ReactDOMServer-dev.classic.js b/compiled/facebook-www/ReactDOMServer-dev.classic.js index 89759fdbb0e64..88809fa35ad4f 100644 --- a/compiled/facebook-www/ReactDOMServer-dev.classic.js +++ b/compiled/facebook-www/ReactDOMServer-dev.classic.js @@ -19,7 +19,7 @@ if (__DEV__) { var React = require("react"); var ReactDOM = require("react-dom"); - var ReactVersion = "18.3.0-www-classic-deec0503"; + var ReactVersion = "18.3.0-www-classic-e27c8048"; // This refers to a WWW module. var warningWWW = require("warning"); @@ -7698,6 +7698,9 @@ if (__DEV__) { var headers = renderState.headers; if (headers) { + // Even if onHeaders throws we don't want to call this again so + // we drop the headers state from this point onwards. + renderState.headers = null; var linkHeader = headers.preconnects; if (headers.fontPreloads) { @@ -7780,7 +7783,6 @@ if (__DEV__) { onHeaders({}); } - renderState.headers = null; return; } } @@ -13219,6 +13221,19 @@ if (__DEV__) { if (request.allPendingTasks === 0) { completeAll(request); } + } + + function safelyEmitEarlyPreloads(request, shellComplete) { + try { + emitEarlyPreloads( + request.renderState, + request.resumableState, + shellComplete + ); + } catch (error) { + // We assume preloads are optimistic and thus non-fatal if errored. + logRecoverableError(request, error); + } } // I extracted this function out because we want to ensure we consistently emit preloads before // transitioning to the next request stage and this transition can happen in multiple places in this // implementation. @@ -13231,11 +13246,7 @@ if (__DEV__) { // we should only be calling completeShell when the shell is complete so we // just use a literal here var shellComplete = true; - emitEarlyPreloads( - request.renderState, - request.resumableState, - shellComplete - ); + safelyEmitEarlyPreloads(request, shellComplete); } // We have completed the shell so the shell can't error anymore. request.onShellError = noop; @@ -13255,11 +13266,7 @@ if (__DEV__) { ? true // Prerender Request, we use the state of the root segment : request.completedRootSegment === null || request.completedRootSegment.status !== POSTPONED; - emitEarlyPreloads( - request.renderState, - request.resumableState, - shellComplete - ); + safelyEmitEarlyPreloads(request, shellComplete); var onAllReady = request.onAllReady; onAllReady(); } @@ -14102,11 +14109,7 @@ if (__DEV__) { function enqueueEarlyPreloadsAfterInitialWork(request) { var shellComplete = request.pendingRootTasks === 0; - emitEarlyPreloads( - request.renderState, - request.resumableState, - shellComplete - ); + safelyEmitEarlyPreloads(request, shellComplete); } function enqueueFlush(request) { diff --git a/compiled/facebook-www/ReactDOMServer-dev.modern.js b/compiled/facebook-www/ReactDOMServer-dev.modern.js index 5f13a8c899ba3..7fb4442e85050 100644 --- a/compiled/facebook-www/ReactDOMServer-dev.modern.js +++ b/compiled/facebook-www/ReactDOMServer-dev.modern.js @@ -19,7 +19,7 @@ if (__DEV__) { var React = require("react"); var ReactDOM = require("react-dom"); - var ReactVersion = "18.3.0-www-modern-7ac3e370"; + var ReactVersion = "18.3.0-www-modern-b8021dcf"; // This refers to a WWW module. var warningWWW = require("warning"); @@ -7698,6 +7698,9 @@ if (__DEV__) { var headers = renderState.headers; if (headers) { + // Even if onHeaders throws we don't want to call this again so + // we drop the headers state from this point onwards. + renderState.headers = null; var linkHeader = headers.preconnects; if (headers.fontPreloads) { @@ -7780,7 +7783,6 @@ if (__DEV__) { onHeaders({}); } - renderState.headers = null; return; } } @@ -12947,6 +12949,19 @@ if (__DEV__) { if (request.allPendingTasks === 0) { completeAll(request); } + } + + function safelyEmitEarlyPreloads(request, shellComplete) { + try { + emitEarlyPreloads( + request.renderState, + request.resumableState, + shellComplete + ); + } catch (error) { + // We assume preloads are optimistic and thus non-fatal if errored. + logRecoverableError(request, error); + } } // I extracted this function out because we want to ensure we consistently emit preloads before // transitioning to the next request stage and this transition can happen in multiple places in this // implementation. @@ -12959,11 +12974,7 @@ if (__DEV__) { // we should only be calling completeShell when the shell is complete so we // just use a literal here var shellComplete = true; - emitEarlyPreloads( - request.renderState, - request.resumableState, - shellComplete - ); + safelyEmitEarlyPreloads(request, shellComplete); } // We have completed the shell so the shell can't error anymore. request.onShellError = noop; @@ -12983,11 +12994,7 @@ if (__DEV__) { ? true // Prerender Request, we use the state of the root segment : request.completedRootSegment === null || request.completedRootSegment.status !== POSTPONED; - emitEarlyPreloads( - request.renderState, - request.resumableState, - shellComplete - ); + safelyEmitEarlyPreloads(request, shellComplete); var onAllReady = request.onAllReady; onAllReady(); } @@ -13830,11 +13837,7 @@ if (__DEV__) { function enqueueEarlyPreloadsAfterInitialWork(request) { var shellComplete = request.pendingRootTasks === 0; - emitEarlyPreloads( - request.renderState, - request.resumableState, - shellComplete - ); + safelyEmitEarlyPreloads(request, shellComplete); } function enqueueFlush(request) { diff --git a/compiled/facebook-www/ReactDOMServer-prod.classic.js b/compiled/facebook-www/ReactDOMServer-prod.classic.js index e4b4d264e7a16..a8567e68b1f26 100644 --- a/compiled/facebook-www/ReactDOMServer-prod.classic.js +++ b/compiled/facebook-www/ReactDOMServer-prod.classic.js @@ -2470,62 +2470,6 @@ function hoistStyleQueueDependency(styleQueue) { function hoistStylesheetDependency(stylesheet) { this.stylesheets.add(stylesheet); } -function emitEarlyPreloads(renderState, resumableState, shellComplete) { - if ((resumableState = renderState.onHeaders)) { - var headers = renderState.headers; - if (headers) { - var linkHeader = headers.preconnects; - headers.fontPreloads && - (linkHeader && (linkHeader += ", "), - (linkHeader += headers.fontPreloads)); - headers.highImagePreloads && - (linkHeader && (linkHeader += ", "), - (linkHeader += headers.highImagePreloads)); - if (!shellComplete) { - shellComplete = renderState.styles.values(); - var queueStep = shellComplete.next(); - a: for ( - ; - 0 < headers.remainingCapacity && !queueStep.done; - queueStep = shellComplete.next() - ) { - queueStep = queueStep.value.sheets.values(); - for ( - var sheetStep = queueStep.next(); - 0 < headers.remainingCapacity && !sheetStep.done; - sheetStep = queueStep.next() - ) { - var sheet = sheetStep.value; - sheetStep = sheet.props; - var key = sheetStep.href; - sheet = sheet.props; - sheet = getPreloadAsHeader(sheet.href, "style", { - crossOrigin: sheet.crossOrigin, - integrity: sheet.integrity, - nonce: sheet.nonce, - type: sheet.type, - fetchPriority: sheet.fetchPriority, - referrerPolicy: sheet.referrerPolicy, - media: sheet.media - }); - if (2 <= (headers.remainingCapacity -= sheet.length)) - (renderState.resets.style[key] = PRELOAD_NO_CREDS), - linkHeader && (linkHeader += ", "), - (linkHeader += sheet), - (renderState.resets.style[key] = - "string" === typeof sheetStep.crossOrigin || - "string" === typeof sheetStep.integrity - ? [sheetStep.crossOrigin, sheetStep.integrity] - : PRELOAD_NO_CREDS); - else break a; - } - } - } - linkHeader ? resumableState({ Link: linkHeader }) : resumableState({}); - renderState.headers = null; - } - } -} function createRenderState(resumableState, generateStaticMarkup) { var idPrefix = resumableState.idPrefix, bootstrapChunks = [], @@ -2539,16 +2483,16 @@ function createRenderState(resumableState, generateStaticMarkup) { "\x3c/script>" ); bootstrapScriptContent = idPrefix + "P:"; - var JSCompiler_object_inline_segmentPrefix_1586 = idPrefix + "S:"; + var JSCompiler_object_inline_segmentPrefix_1602 = idPrefix + "S:"; idPrefix += "B:"; - var JSCompiler_object_inline_preconnects_1601 = new Set(), - JSCompiler_object_inline_fontPreloads_1602 = new Set(), - JSCompiler_object_inline_highImagePreloads_1603 = new Set(), - JSCompiler_object_inline_styles_1604 = new Map(), - JSCompiler_object_inline_bootstrapScripts_1605 = new Set(), - JSCompiler_object_inline_scripts_1606 = new Set(), - JSCompiler_object_inline_bulkPreloads_1607 = new Set(), - JSCompiler_object_inline_preloads_1608 = { + var JSCompiler_object_inline_preconnects_1617 = new Set(), + JSCompiler_object_inline_fontPreloads_1618 = new Set(), + JSCompiler_object_inline_highImagePreloads_1619 = new Set(), + JSCompiler_object_inline_styles_1620 = new Map(), + JSCompiler_object_inline_bootstrapScripts_1621 = new Set(), + JSCompiler_object_inline_scripts_1622 = new Set(), + JSCompiler_object_inline_bulkPreloads_1623 = new Set(), + JSCompiler_object_inline_preloads_1624 = { images: new Map(), stylesheets: new Map(), scripts: new Map(), @@ -2585,7 +2529,7 @@ function createRenderState(resumableState, generateStaticMarkup) { scriptConfig.moduleScriptResources[href] = null; scriptConfig = []; pushLinkImpl(scriptConfig, props); - JSCompiler_object_inline_bootstrapScripts_1605.add(scriptConfig); + JSCompiler_object_inline_bootstrapScripts_1621.add(scriptConfig); bootstrapChunks.push('