Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Reject any new Chunks not yet discovered at the time of reportGlobalE…
…rror #31840
  • Loading branch information
sebmarkbage committed Dec 19, 2024
commit ab1c43c643e967b21546f7b76748f8e7df1da277
16 changes: 14 additions & 2 deletions packages/react-client/src/ReactFlightClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,8 @@ export type Response = {
_rowTag: number, // 0 indicates that we're currently parsing the row ID
_rowLength: number, // remaining bytes in the row. 0 indicates that we're looking for a newline.
_buffer: Array<Uint8Array>, // chunks received so far as part of this row
_closed: boolean,
_closedReason: mixed,
_tempRefs: void | TemporaryReferenceSet, // the set temporary references can be resolved from
_timeOrigin: number, // Profiling-only
_debugRootOwner?: null | ReactComponentInfo, // DEV-only
Expand Down Expand Up @@ -358,7 +360,7 @@ function createBlockedChunk<T>(response: Response): BlockedChunk<T> {

function createErrorChunk<T>(
response: Response,
error: Error | Postpone,
error: mixed,
): ErroredChunk<T> {
// $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors
return new ReactPromise(ERRORED, null, error, response);
Expand Down Expand Up @@ -639,6 +641,8 @@ function initializeModuleChunk<T>(chunk: ResolvedModuleChunk<T>): void {
// Report that any missing chunks in the model is now going to throw this
// error upon read. Also notify any pending promises.
export function reportGlobalError(response: Response, error: Error): void {
response._closed = true;
response._closedReason = error;
response._chunks.forEach(chunk => {
// If this chunk was already resolved or errored, it won't
// trigger an error but if it wasn't then we need to
Expand Down Expand Up @@ -913,7 +917,13 @@ function getChunk(response: Response, id: number): SomeChunk<any> {
const chunks = response._chunks;
let chunk = chunks.get(id);
if (!chunk) {
chunk = createPendingChunk(response);
if (response._closed) {
// We have already errored the response and we're not going to get
// anything more streaming in so this will immediately error.
chunk = createErrorChunk(response, response._closedReason);
} else {
chunk = createPendingChunk(response);
}
chunks.set(id, chunk);
}
return chunk;
Expand Down Expand Up @@ -1640,6 +1650,8 @@ function ResponseInstance(
this._rowTag = 0;
this._rowLength = 0;
this._buffer = [];
this._closed = false;
this._closedReason = null;
this._tempRefs = temporaryReferences;
if (enableProfilerTimer && enableComponentPerformanceTrack) {
this._timeOrigin = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1261,4 +1261,52 @@ describe('ReactFlightDOMEdge', () => {
div.innerHTML = result;
expect(div.textContent).toBe('loading...');
});

// @gate enableHalt
it('should abort parsing an incomplete prerender payload', async () => {
const infinitePromise = new Promise(() => {});
const controller = new AbortController();
const errors = [];
const {pendingResult} = await serverAct(async () => {
// destructure trick to avoid the act scope from awaiting the returned value
return {
pendingResult: ReactServerDOMStaticServer.unstable_prerender(
{promise: infinitePromise},
webpackMap,
{
signal: controller.signal,
onError(err) {
errors.push(err);
},
},
),
};
});

controller.abort();
const {prelude} = await pendingResult;

expect(errors).toEqual([]);

const response = ReactServerDOMClient.createFromReadableStream(prelude, {
serverConsumerManifest: {
moduleMap: {},
moduleLoading: {},
},
});

// Wait for the stream to finish and therefore abort before we try to .then the response.
await 0;

const result = await response;

let error = null;
try {
await result.promise;
} catch (x) {
error = x;
}
expect(error).not.toBe(null);
expect(error.message).toBe('Connection closed.');
});
});
Loading