From 9c631ea744cdb3b08565f88c7f34fcdbcbc46a1f Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 09:51:31 -0400 Subject: [PATCH] fix: data corruption in `protocol.handle` (#41932) * fix: data corruption in protocol.handle Co-authored-by: Jeremy Rose * slice instead of subarray Co-authored-by: Jeremy Rose --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Jeremy Rose --- lib/browser/api/protocol.ts | 2 +- spec/api-protocol-spec.ts | 43 +++++++++++++++++++++++++++++++++++++ spec/index.js | 1 + 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/lib/browser/api/protocol.ts b/lib/browser/api/protocol.ts index 0bdec7298332..1b7f00b433c0 100644 --- a/lib/browser/api/protocol.ts +++ b/lib/browser/api/protocol.ts @@ -18,7 +18,7 @@ function makeStreamFromPipe (pipe: any): ReadableStream { try { const rv = await pipe.read(buf); if (rv > 0) { - controller.enqueue(buf.subarray(0, rv)); + controller.enqueue(buf.slice(0, rv)); } else { controller.close(); } diff --git a/spec/api-protocol-spec.ts b/spec/api-protocol-spec.ts index 348f6c48e6b7..122cc4e84f0d 100644 --- a/spec/api-protocol-spec.ts +++ b/spec/api-protocol-spec.ts @@ -1381,6 +1381,49 @@ describe('protocol module', () => { expect(body).to.equal(text); }); + it('can receive stream request body asynchronously', async () => { + let done: any; + const requestReceived: Promise = new Promise(resolve => { done = resolve; }); + protocol.handle('http-like', async (req) => { + const chunks = []; + for await (const chunk of (req.body as any)) { + chunks.push(chunk); + } + done(chunks); + return new Response('ok'); + }); + defer(() => { protocol.unhandle('http-like'); }); + const w = new BrowserWindow({ show: false }); + w.loadURL('about:blank'); + const expectedHashChunks = await w.webContents.executeJavaScript(` + const dataStream = () => + new ReadableStream({ + async start(controller) { + for (let i = 0; i < 10; i++) { controller.enqueue(Array(1024 * 128).fill(+i).join("\\n")); } + controller.close(); + }, + }).pipeThrough(new TextEncoderStream()); + fetch( + new Request("http-like://host", { + method: "POST", + body: dataStream(), + duplex: "half", + }) + ); + (async () => { + const chunks = [] + for await (const chunk of dataStream()) { + chunks.push(chunk); + } + return chunks; + })() + `); + const expectedHash = Buffer.from(await crypto.subtle.digest('SHA-256', Buffer.concat(expectedHashChunks))).toString('hex'); + const body = Buffer.concat(await requestReceived); + const actualHash = Buffer.from(await crypto.subtle.digest('SHA-256', Buffer.from(body))).toString('hex'); + expect(actualHash).to.equal(expectedHash); + }); + it('can receive multi-part postData from loadURL', async () => { protocol.handle('test-scheme', (req) => new Response(req.body)); defer(() => { protocol.unhandle('test-scheme'); }); diff --git a/spec/index.js b/spec/index.js index 259bca132c0f..2082ea9b2a39 100644 --- a/spec/index.js +++ b/spec/index.js @@ -40,6 +40,7 @@ protocol.registerSchemesAsPrivileged([ { scheme: global.standardScheme, privileges: { standard: true, secure: true, stream: false } }, { scheme: global.zoomScheme, privileges: { standard: true, secure: true } }, { scheme: global.serviceWorkerScheme, privileges: { allowServiceWorkers: true, standard: true, secure: true } }, + { scheme: 'http-like', privileges: { standard: true, secure: true, corsEnabled: true, supportFetchAPI: true } }, { scheme: 'cors-blob', privileges: { corsEnabled: true, supportFetchAPI: true } }, { scheme: 'cors', privileges: { corsEnabled: true, supportFetchAPI: true } }, { scheme: 'no-cors', privileges: { supportFetchAPI: true } },