Skip to content

Commit 0648eb9

Browse files
committed
Encode binary streams as a single collapsed Blob
Since we buffer anyway, we might as well combine the chunks of binary streams. Ideally, we'd be able to just use the stream from the Blob but Node.js doesn't return byob streams from Blob. Additionally, we don't actually stream the content of Blobs due to the layering with busboy atm. We could do that for binary streams in particular by replacing the File layering with a stream.
1 parent f0b6ff2 commit 0648eb9

File tree

1 file changed

+49
-16
lines changed

1 file changed

+49
-16
lines changed

packages/react-client/src/ReactFlightReplyClient.js

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ export function processReply(
200200
return '$' + tag + blobId.toString(16);
201201
}
202202

203-
function serializeReadableStream(stream: ReadableStream): string {
203+
function serializeBinaryReader(reader: any): string {
204204
if (formData === null) {
205205
// Upgrade to use FormData to allow us to stream this value.
206206
formData = new FormData();
@@ -210,23 +210,43 @@ export function processReply(
210210
pendingParts++;
211211
const streamId = nextPartId++;
212212

213-
// Detect if this is a BYOB stream. BYOB streams should be able to be read as bytes on the
214-
// receiving side. It also implies that different chunks can be split up or merged as opposed
215-
// to a readable stream that happens to have Uint8Array as the type which might expect it to be
216-
// received in the same slices.
217-
// $FlowFixMe: This is a Node.js extension.
218-
let supportsBYOB: void | boolean = stream.supportsBYOB;
219-
if (supportsBYOB === undefined) {
220-
try {
221-
// $FlowFixMe[extra-arg]: This argument is accepted.
222-
stream.getReader({mode: 'byob'}).releaseLock();
223-
supportsBYOB = true;
224-
} catch (x) {
225-
supportsBYOB = false;
213+
const buffer = [];
214+
215+
function progress(entry: {done: boolean, value: ReactServerValue, ...}) {
216+
if (entry.done) {
217+
const blobId = nextPartId++;
218+
// eslint-disable-next-line react-internal/safe-string-coercion
219+
data.append(formFieldPrefix + blobId, new Blob(buffer));
220+
// eslint-disable-next-line react-internal/safe-string-coercion
221+
data.append(
222+
formFieldPrefix + streamId,
223+
'"$o' + blobId.toString(16) + '"',
224+
);
225+
// eslint-disable-next-line react-internal/safe-string-coercion
226+
data.append(formFieldPrefix + streamId, 'C'); // Close signal
227+
pendingParts--;
228+
if (pendingParts === 0) {
229+
resolve(data);
230+
}
231+
} else {
232+
buffer.push(entry.value);
233+
reader.read(new Uint8Array(1024)).then(progress, reject);
226234
}
227235
}
236+
reader.read(new Uint8Array(1024)).then(progress, reject);
228237

229-
const reader = stream.getReader();
238+
return '$r' + streamId.toString(16);
239+
}
240+
241+
function serializeReader(reader: ReadableStreamReader): string {
242+
if (formData === null) {
243+
// Upgrade to use FormData to allow us to stream this value.
244+
formData = new FormData();
245+
}
246+
const data = formData;
247+
248+
pendingParts++;
249+
const streamId = nextPartId++;
230250

231251
function progress(entry: {done: boolean, value: ReactServerValue, ...}) {
232252
if (entry.done) {
@@ -250,7 +270,20 @@ export function processReply(
250270
}
251271
reader.read().then(progress, reject);
252272

253-
return '$' + (supportsBYOB ? 'r' : 'R') + streamId.toString(16);
273+
return '$R' + streamId.toString(16);
274+
}
275+
276+
function serializeReadableStream(stream: ReadableStream): string {
277+
// Detect if this is a BYOB stream. BYOB streams should be able to be read as bytes on the
278+
// receiving side. For binary streams, we serialize them as plain Blobs.
279+
let binaryReader;
280+
try {
281+
// $FlowFixMe[extra-arg]: This argument is accepted.
282+
binaryReader = stream.getReader({mode: 'byob'});
283+
} catch (x) {
284+
return serializeReader(stream.getReader());
285+
}
286+
return serializeBinaryReader(binaryReader);
254287
}
255288

256289
function serializeAsyncIterable(

0 commit comments

Comments
 (0)