mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
Patch FlightReplyServer with fixes from ReactFlightClient (#35277)
FlightReplyServer are for client->server and ReactFlightClient is for server->client. They're not 100% symmetrical. We did a number of refactors to ReactFlightClient in PRs like #29823 and #33664 to change the structure of the resolution. This PR brings those changes to synchronize the two approaches. Which addresses deep resolution of cycles and deferred error handling. This also fixes a critical security vulnerability.
This commit is contained in:
committed by
GitHub
parent
36df5e8b42
commit
7dc903cd29
@@ -344,16 +344,23 @@ function decodeReplyFromBusboy<T>(
|
||||
// we queue any fields we receive until the previous file is done.
|
||||
queuedFields.push(name, value);
|
||||
} else {
|
||||
resolveField(response, name, value);
|
||||
try {
|
||||
resolveField(response, name, value);
|
||||
} catch (error) {
|
||||
busboyStream.destroy(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
busboyStream.on('file', (name, value, {filename, encoding, mimeType}) => {
|
||||
if (encoding.toLowerCase() === 'base64') {
|
||||
throw new Error(
|
||||
"React doesn't accept base64 encoded file uploads because we don't expect " +
|
||||
"form data passed from a browser to ever encode data that way. If that's " +
|
||||
'the wrong assumption, we can easily fix it.',
|
||||
busboyStream.destroy(
|
||||
new Error(
|
||||
"React doesn't accept base64 encoded file uploads because we don't expect " +
|
||||
"form data passed from a browser to ever encode data that way. If that's " +
|
||||
'the wrong assumption, we can easily fix it.',
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
pendingFiles++;
|
||||
const file = resolveFileInfo(response, name, filename, mimeType);
|
||||
@@ -361,14 +368,18 @@ function decodeReplyFromBusboy<T>(
|
||||
resolveFileChunk(response, file, chunk);
|
||||
});
|
||||
value.on('end', () => {
|
||||
resolveFileComplete(response, name, file);
|
||||
pendingFiles--;
|
||||
if (pendingFiles === 0) {
|
||||
// Release any queued fields
|
||||
for (let i = 0; i < queuedFields.length; i += 2) {
|
||||
resolveField(response, queuedFields[i], queuedFields[i + 1]);
|
||||
try {
|
||||
resolveFileComplete(response, name, file);
|
||||
pendingFiles--;
|
||||
if (pendingFiles === 0) {
|
||||
// Release any queued fields
|
||||
for (let i = 0; i < queuedFields.length; i += 2) {
|
||||
resolveField(response, queuedFields[i], queuedFields[i + 1]);
|
||||
}
|
||||
queuedFields.length = 0;
|
||||
}
|
||||
queuedFields.length = 0;
|
||||
} catch (error) {
|
||||
busboyStream.destroy(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,6 +19,8 @@ import {
|
||||
} from '../shared/ReactFlightImportMetadata';
|
||||
import {prepareDestinationWithChunks} from 'react-client/src/ReactFlightClientConfig';
|
||||
|
||||
import hasOwnProperty from 'shared/hasOwnProperty';
|
||||
|
||||
export type ServerManifest = {
|
||||
[string]: Array<string>,
|
||||
};
|
||||
@@ -78,7 +80,10 @@ export function preloadModule<T>(
|
||||
|
||||
export function requireModule<T>(metadata: ClientReference<T>): T {
|
||||
const moduleExports = parcelRequire(metadata[ID]);
|
||||
return moduleExports[metadata[NAME]];
|
||||
if (hasOwnProperty.call(moduleExports, metadata[NAME])) {
|
||||
return moduleExports[metadata[NAME]];
|
||||
}
|
||||
return (undefined: any);
|
||||
}
|
||||
|
||||
export function getModuleDebugInfo<T>(
|
||||
|
||||
@@ -572,16 +572,23 @@ export function decodeReplyFromBusboy<T>(
|
||||
// we queue any fields we receive until the previous file is done.
|
||||
queuedFields.push(name, value);
|
||||
} else {
|
||||
resolveField(response, name, value);
|
||||
try {
|
||||
resolveField(response, name, value);
|
||||
} catch (error) {
|
||||
busboyStream.destroy(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
busboyStream.on('file', (name, value, {filename, encoding, mimeType}) => {
|
||||
if (encoding.toLowerCase() === 'base64') {
|
||||
throw new Error(
|
||||
"React doesn't accept base64 encoded file uploads because we don't expect " +
|
||||
"form data passed from a browser to ever encode data that way. If that's " +
|
||||
'the wrong assumption, we can easily fix it.',
|
||||
busboyStream.destroy(
|
||||
new Error(
|
||||
"React doesn't accept base64 encoded file uploads because we don't expect " +
|
||||
"form data passed from a browser to ever encode data that way. If that's " +
|
||||
'the wrong assumption, we can easily fix it.',
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
pendingFiles++;
|
||||
const file = resolveFileInfo(response, name, filename, mimeType);
|
||||
@@ -589,14 +596,18 @@ export function decodeReplyFromBusboy<T>(
|
||||
resolveFileChunk(response, file, chunk);
|
||||
});
|
||||
value.on('end', () => {
|
||||
resolveFileComplete(response, name, file);
|
||||
pendingFiles--;
|
||||
if (pendingFiles === 0) {
|
||||
// Release any queued fields
|
||||
for (let i = 0; i < queuedFields.length; i += 2) {
|
||||
resolveField(response, queuedFields[i], queuedFields[i + 1]);
|
||||
try {
|
||||
resolveFileComplete(response, name, file);
|
||||
pendingFiles--;
|
||||
if (pendingFiles === 0) {
|
||||
// Release any queued fields
|
||||
for (let i = 0; i < queuedFields.length; i += 2) {
|
||||
resolveField(response, queuedFields[i], queuedFields[i + 1]);
|
||||
}
|
||||
queuedFields.length = 0;
|
||||
}
|
||||
queuedFields.length = 0;
|
||||
} catch (error) {
|
||||
busboyStream.destroy(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -34,6 +34,8 @@ import {
|
||||
addChunkDebugInfo,
|
||||
} from 'react-client/src/ReactFlightClientConfig';
|
||||
|
||||
import hasOwnProperty from 'shared/hasOwnProperty';
|
||||
|
||||
export type ServerConsumerModuleMap = null | {
|
||||
[clientId: string]: {
|
||||
[clientExportName: string]: ClientReferenceManifestEntry,
|
||||
@@ -245,7 +247,10 @@ export function requireModule<T>(metadata: ClientReference<T>): T {
|
||||
// default property of this if it was an ESM interop module.
|
||||
return moduleExports.__esModule ? moduleExports.default : moduleExports;
|
||||
}
|
||||
return moduleExports[metadata[NAME]];
|
||||
if (hasOwnProperty.call(moduleExports, metadata[NAME])) {
|
||||
return moduleExports[metadata[NAME]];
|
||||
}
|
||||
return (undefined: any);
|
||||
}
|
||||
|
||||
export function getModuleDebugInfo<T>(
|
||||
|
||||
@@ -564,16 +564,23 @@ function decodeReplyFromBusboy<T>(
|
||||
// we queue any fields we receive until the previous file is done.
|
||||
queuedFields.push(name, value);
|
||||
} else {
|
||||
resolveField(response, name, value);
|
||||
try {
|
||||
resolveField(response, name, value);
|
||||
} catch (error) {
|
||||
busboyStream.destroy(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
busboyStream.on('file', (name, value, {filename, encoding, mimeType}) => {
|
||||
if (encoding.toLowerCase() === 'base64') {
|
||||
throw new Error(
|
||||
"React doesn't accept base64 encoded file uploads because we don't expect " +
|
||||
"form data passed from a browser to ever encode data that way. If that's " +
|
||||
'the wrong assumption, we can easily fix it.',
|
||||
busboyStream.destroy(
|
||||
new Error(
|
||||
"React doesn't accept base64 encoded file uploads because we don't expect " +
|
||||
"form data passed from a browser to ever encode data that way. If that's " +
|
||||
'the wrong assumption, we can easily fix it.',
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
pendingFiles++;
|
||||
const file = resolveFileInfo(response, name, filename, mimeType);
|
||||
@@ -581,14 +588,18 @@ function decodeReplyFromBusboy<T>(
|
||||
resolveFileChunk(response, file, chunk);
|
||||
});
|
||||
value.on('end', () => {
|
||||
resolveFileComplete(response, name, file);
|
||||
pendingFiles--;
|
||||
if (pendingFiles === 0) {
|
||||
// Release any queued fields
|
||||
for (let i = 0; i < queuedFields.length; i += 2) {
|
||||
resolveField(response, queuedFields[i], queuedFields[i + 1]);
|
||||
try {
|
||||
resolveFileComplete(response, name, file);
|
||||
pendingFiles--;
|
||||
if (pendingFiles === 0) {
|
||||
// Release any queued fields
|
||||
for (let i = 0; i < queuedFields.length; i += 2) {
|
||||
resolveField(response, queuedFields[i], queuedFields[i + 1]);
|
||||
}
|
||||
queuedFields.length = 0;
|
||||
}
|
||||
queuedFields.length = 0;
|
||||
} catch (error) {
|
||||
busboyStream.destroy(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,6 +24,8 @@ import {
|
||||
} from '../shared/ReactFlightImportMetadata';
|
||||
import {prepareDestinationWithChunks} from 'react-client/src/ReactFlightClientConfig';
|
||||
|
||||
import hasOwnProperty from 'shared/hasOwnProperty';
|
||||
|
||||
export type ServerConsumerModuleMap = {
|
||||
[clientId: string]: {
|
||||
[clientExportName: string]: ClientReference<any>,
|
||||
@@ -158,7 +160,10 @@ export function requireModule<T>(metadata: ClientReference<T>): T {
|
||||
// default property of this if it was an ESM interop module.
|
||||
return moduleExports.default;
|
||||
}
|
||||
return moduleExports[metadata.name];
|
||||
if (hasOwnProperty.call(moduleExports, metadata.name)) {
|
||||
return moduleExports[metadata.name];
|
||||
}
|
||||
return (undefined: any);
|
||||
}
|
||||
|
||||
export function getModuleDebugInfo<T>(metadata: ClientReference<T>): null {
|
||||
|
||||
@@ -34,6 +34,8 @@ import {
|
||||
addChunkDebugInfo,
|
||||
} from 'react-client/src/ReactFlightClientConfig';
|
||||
|
||||
import hasOwnProperty from 'shared/hasOwnProperty';
|
||||
|
||||
export type ServerConsumerModuleMap = null | {
|
||||
[clientId: string]: {
|
||||
[clientExportName: string]: ClientReferenceManifestEntry,
|
||||
@@ -253,7 +255,10 @@ export function requireModule<T>(metadata: ClientReference<T>): T {
|
||||
// default property of this if it was an ESM interop module.
|
||||
return moduleExports.__esModule ? moduleExports.default : moduleExports;
|
||||
}
|
||||
return moduleExports[metadata[NAME]];
|
||||
if (hasOwnProperty.call(moduleExports, metadata[NAME])) {
|
||||
return moduleExports[metadata[NAME]];
|
||||
}
|
||||
return (undefined: any);
|
||||
}
|
||||
|
||||
export function getModuleDebugInfo<T>(
|
||||
|
||||
@@ -564,16 +564,23 @@ function decodeReplyFromBusboy<T>(
|
||||
// we queue any fields we receive until the previous file is done.
|
||||
queuedFields.push(name, value);
|
||||
} else {
|
||||
resolveField(response, name, value);
|
||||
try {
|
||||
resolveField(response, name, value);
|
||||
} catch (error) {
|
||||
busboyStream.destroy(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
busboyStream.on('file', (name, value, {filename, encoding, mimeType}) => {
|
||||
if (encoding.toLowerCase() === 'base64') {
|
||||
throw new Error(
|
||||
"React doesn't accept base64 encoded file uploads because we don't expect " +
|
||||
"form data passed from a browser to ever encode data that way. If that's " +
|
||||
'the wrong assumption, we can easily fix it.',
|
||||
busboyStream.destroy(
|
||||
new Error(
|
||||
"React doesn't accept base64 encoded file uploads because we don't expect " +
|
||||
"form data passed from a browser to ever encode data that way. If that's " +
|
||||
'the wrong assumption, we can easily fix it.',
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
pendingFiles++;
|
||||
const file = resolveFileInfo(response, name, filename, mimeType);
|
||||
@@ -581,14 +588,18 @@ function decodeReplyFromBusboy<T>(
|
||||
resolveFileChunk(response, file, chunk);
|
||||
});
|
||||
value.on('end', () => {
|
||||
resolveFileComplete(response, name, file);
|
||||
pendingFiles--;
|
||||
if (pendingFiles === 0) {
|
||||
// Release any queued fields
|
||||
for (let i = 0; i < queuedFields.length; i += 2) {
|
||||
resolveField(response, queuedFields[i], queuedFields[i + 1]);
|
||||
try {
|
||||
resolveFileComplete(response, name, file);
|
||||
pendingFiles--;
|
||||
if (pendingFiles === 0) {
|
||||
// Release any queued fields
|
||||
for (let i = 0; i < queuedFields.length; i += 2) {
|
||||
resolveField(response, queuedFields[i], queuedFields[i + 1]);
|
||||
}
|
||||
queuedFields.length = 0;
|
||||
}
|
||||
queuedFields.length = 0;
|
||||
} catch (error) {
|
||||
busboyStream.destroy(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
828
packages/react-server/src/ReactFlightReplyServer.js
vendored
828
packages/react-server/src/ReactFlightReplyServer.js
vendored
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user