From 401cd9c0ee8d9cc58c00157d9374df9e122e886a Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Fri, 28 Jan 2022 12:58:58 -0800 Subject: [PATCH] fix(fetch): do not throw on empty response body with br encoding (#11708) --- packages/playwright-core/src/server/fetch.ts | 35 ++++++++++++-------- tests/global-fetch.spec.ts | 21 +++++++++++- 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/packages/playwright-core/src/server/fetch.ts b/packages/playwright-core/src/server/fetch.ts index c1e15aefa2..f020062694 100644 --- a/packages/playwright-core/src/server/fetch.ts +++ b/packages/playwright-core/src/server/fetch.ts @@ -328,6 +328,26 @@ export abstract class APIRequestContext extends SdkObject { } response.on('aborted', () => reject(new Error('aborted'))); + const chunks: Buffer[] = []; + const notifyBodyFinished = () => { + const body = Buffer.concat(chunks); + notifyRequestFinished(body); + fulfill({ + url: response.url || url.toString(), + status: response.statusCode || 0, + statusText: response.statusMessage || '', + headers: toHeadersArray(response.rawHeaders), + body + }); + }; + + // These requests don't have response body. + if (['HEAD', 'PUT', 'TRACE'].includes(options.method!)) { + notifyBodyFinished(); + request.destroy(); + return; + } + let body: Readable = response; let transform: Transform | undefined; const encoding = response.headers['content-encoding']; @@ -348,20 +368,9 @@ export abstract class APIRequestContext extends SdkObject { }); } - const chunks: Buffer[] = []; body.on('data', chunk => chunks.push(chunk)); - body.on('end', () => { - const body = Buffer.concat(chunks); - notifyRequestFinished(body); - fulfill({ - url: response.url || url.toString(), - status: response.statusCode || 0, - statusText: response.statusMessage || '', - headers: toHeadersArray(response.rawHeaders), - body - }); - }); - body.on('error',reject); + body.on('end', notifyBodyFinished); + body.on('error', reject); }); request.on('error', reject); diff --git a/tests/global-fetch.spec.ts b/tests/global-fetch.spec.ts index 3d8da215cb..11ea2a36fb 100644 --- a/tests/global-fetch.spec.ts +++ b/tests/global-fetch.spec.ts @@ -50,7 +50,7 @@ for (const method of ['fetch', 'delete', 'get', 'head', 'patch', 'post', 'put'] expect(response.ok()).toBeTruthy(); expect(response.headers()['content-type']).toBe('application/json; charset=utf-8'); expect(response.headersArray()).toContainEqual({ name: 'Content-Type', value: 'application/json; charset=utf-8' }); - expect(await response.text()).toBe(method === 'head' ? '' : '{"foo": "bar"}\n'); + expect(await response.text()).toBe(['head', 'put'].includes(method) ? '' : '{"foo": "bar"}\n'); }); } @@ -344,3 +344,22 @@ it(`should have nice toString`, async ({ playwright, server }) => { expect(str).toContain(` ${name}: ${value}`); await request.dispose(); }); + +it('should not fail on empty body with encoding', async ({ playwright, server }) => { + const request = await playwright.request.newContext(); + for (const method of ['head', 'put']) { + for (const encoding of ['br', 'gzip', 'deflate']) { + server.setRoute('/empty.html', (req, res) => { + res.writeHead(200, { + 'Content-Encoding': encoding, + 'Content-Type': 'text/plain', + }); + res.end(); + }); + const response = await request[method](server.EMPTY_PAGE); + expect(response.status()).toBe(200); + expect((await response.body()).length).toBe(0); + } + } + await request.dispose(); +});