From 042896472b8ae233437d9e3e937cdae9fdce954b Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Mon, 20 May 2024 16:36:57 -0700 Subject: [PATCH] fix: route.continue should not change multipart form data body (#30863) The bug was fixed in https://github.com/microsoft/playwright/pull/30734. This PR adds a test and updates interception logic to not send post data when it is not modified. Fixes https://github.com/microsoft/playwright/issues/30788 --- .../playwright-core/src/client/network.ts | 5 +-- tests/page/page-request-continue.spec.ts | 43 +++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/packages/playwright-core/src/client/network.ts b/packages/playwright-core/src/client/network.ts index 4e22a27dd6f54..36ade499cfbd9 100644 --- a/packages/playwright-core/src/client/network.ts +++ b/packages/playwright-core/src/client/network.ts @@ -101,7 +101,6 @@ export class Request extends ChannelOwner implements ap if (this._redirectedFrom) this._redirectedFrom._redirectedTo = this; this._provisionalHeaders = new RawHeaders(initializer.headers); - this._fallbackOverrides.postDataBuffer = initializer.postData; this._timing = { startTime: 0, domainLookupStart: -1, @@ -128,11 +127,11 @@ export class Request extends ChannelOwner implements ap } postData(): string | null { - return this._fallbackOverrides.postDataBuffer?.toString('utf-8') || null; + return (this._fallbackOverrides.postDataBuffer || this._initializer.postData)?.toString('utf-8') || null; } postDataBuffer(): Buffer | null { - return this._fallbackOverrides.postDataBuffer || null; + return this._fallbackOverrides.postDataBuffer || this._initializer.postData || null; } postDataJSON(): Object | null { diff --git a/tests/page/page-request-continue.spec.ts b/tests/page/page-request-continue.spec.ts index 1b7f78e40d17d..9230bb5fa8666 100644 --- a/tests/page/page-request-continue.spec.ts +++ b/tests/page/page-request-continue.spec.ts @@ -477,3 +477,46 @@ it('should intercept css variable with background url', async ({ page, server }) await page.waitForTimeout(1000); expect(interceptedRequests).toBe(1); }); + +it('continue should not change multipart/form-data body', async ({ page, server, browserName }) => { + it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/19158' }); + await page.goto(server.EMPTY_PAGE); + server.setRoute('/upload', (request, response) => { + response.writeHead(200, { 'Content-Type': 'text/plain' }); + response.end('done'); + }); + async function sendFormData() { + const reqPromise = server.waitForRequest('/upload'); + const status = await page.evaluate(async () => { + const newFile = new File(['file content'], 'file.txt'); + const formData = new FormData(); + formData.append('file', newFile); + const response = await fetch('/upload', { + method: 'POST', + credentials: 'include', + body: formData, + }); + return response.status; + }); + const req = await reqPromise; + expect(status).toBe(200); + return req; + } + const reqBefore = await sendFormData(); + await page.route('**/*', async route => { + await route.continue(); + }); + const reqAfter = await sendFormData(); + const fileContent = [ + 'Content-Disposition: form-data; name=\"file\"; filename=\"file.txt\"', + 'Content-Type: application/octet-stream', + '', + 'file content', + '------'].join('\r\n'); + expect.soft((await reqBefore.postBody).toString('utf8')).toContain(fileContent); + expect.soft((await reqAfter.postBody).toString('utf8')).toContain(fileContent); + // Firefox sends a bit longer boundary. + const expectedLength = browserName === 'firefox' ? '246' : '208'; + expect.soft(reqBefore.headers['content-length']).toBe(expectedLength); + expect.soft(reqAfter.headers['content-length']).toBe(expectedLength); +});