From a3c6553743dd4f604659be229f8c594b56fed6e5 Mon Sep 17 00:00:00 2001 From: Sophie Alpert Date: Thu, 23 Feb 2023 22:37:09 -0800 Subject: [PATCH 1/2] [Fizz Node] Fix null bytes written at text chunk boundaries We encode strings 2048 UTF-8 bytes at a time. If the string we are encoding crosses to the next chunk but the current chunk doesn't fit an integral number of characters, we need to make sure not to send the whole buffer, only the bytes that are actually meaningful. Fixes #24985. I was able to verify that this fixes the repro shared in the issue (be careful when testing because the null bytes do not show when printed to my terminal, at least). However, I don't see a clear way to add a test for this that will be resilient to small changes in how we encode the markup (since it depends on where specific multibyte characters fall against the 2048-byte boundaries). --- packages/react-server/src/ReactServerStreamConfigNode.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/react-server/src/ReactServerStreamConfigNode.js b/packages/react-server/src/ReactServerStreamConfigNode.js index 807dafe5edf6e..16a773314d679 100644 --- a/packages/react-server/src/ReactServerStreamConfigNode.js +++ b/packages/react-server/src/ReactServerStreamConfigNode.js @@ -75,12 +75,14 @@ function writeStringChunk(destination: Destination, stringChunk: string) { writtenBytes += written; if (read < stringChunk.length) { - writeToDestination(destination, (currentView: any)); + writeToDestination( + destination, + (currentView: any).subarray(0, writtenBytes), + ); currentView = new Uint8Array(VIEW_SIZE); writtenBytes = textEncoder.encodeInto( stringChunk.slice(read), - // $FlowFixMe[incompatible-call] found when upgrading Flow - currentView, + (currentView: any), ).written; } From ba8b1d208d3d64169bcf4c1ba87a30d4ce5338b0 Mon Sep 17 00:00:00 2001 From: Sophie Alpert Date: Fri, 24 Feb 2023 11:19:48 -0800 Subject: [PATCH 2/2] Figured out how to write a test that should work OK --- .../src/__tests__/ReactDOMFizzServerNode-test.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServerNode-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServerNode-test.js index 2f0d5bb98ce00..11c7043e2b9de 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServerNode-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServerNode-test.js @@ -635,4 +635,17 @@ describe('ReactDOMFizzServerNode', () => { expect(rendered).toBe(false); expect(isComplete).toBe(true); }); + + it('should encode multibyte characters correctly without nulls (#24985)', () => { + const {writable, output} = getTestWritable(); + const {pipe} = ReactDOMFizzServer.renderToPipeableStream( +
{Array(700).fill('ののの')}
, + ); + pipe(writable); + jest.runAllTimers(); + expect(output.result.indexOf('\u0000')).toBe(-1); + expect(output.result).toEqual( + '
' + Array(700).fill('ののの').join('') + '
', + ); + }); });