From c5167295443768a9607451fb18022906dba151ec Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Mon, 4 Oct 2021 13:19:05 -0700 Subject: [PATCH] fix(interception): make set-cookie work in chromium (#9299) --- src/server/chromium/crNetworkManager.ts | 17 ++++++++- tests/page/page-request-fulfill.spec.ts | 51 +++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/server/chromium/crNetworkManager.ts b/src/server/chromium/crNetworkManager.ts index 19246286c0..6e3bfa5287 100644 --- a/src/server/chromium/crNetworkManager.ts +++ b/src/server/chromium/crNetworkManager.ts @@ -514,13 +514,14 @@ class RouteImpl implements network.RouteDelegate { this._wasFulfilled = true; const body = response.isBase64 ? response.body : Buffer.from(response.body).toString('base64'); + const responseHeaders = splitSetCookieHeader(response.headers); // In certain cases, protocol will return error if the request was already canceled // or the page was closed. We should tolerate these errors. await this._client._sendMayFail('Fetch.fulfillRequest', { requestId: this._interceptionId!, responseCode: response.status, responsePhrase: network.STATUS_TEXTS[String(response.status)], - responseHeaders: response.headers, + responseHeaders, body, }); } @@ -537,6 +538,20 @@ class RouteImpl implements network.RouteDelegate { } } +function splitSetCookieHeader(headers: types.HeadersArray): types.HeadersArray { + const index = headers.findIndex(({ name }) => name.toLowerCase() === 'set-cookie'); + if (index === -1) + return headers; + + const header = headers[index]; + const values = header.value.split('\n'); + if (values.length === 1) + return headers; + const result = headers.slice(); + result.splice(index, 1, ...values.map(value => ({ name: header.name, value }))); + return result; +} + const errorReasons: { [reason: string]: Protocol.Network.ErrorReason } = { 'aborted': 'Aborted', 'accessdenied': 'AccessDenied', diff --git a/tests/page/page-request-fulfill.spec.ts b/tests/page/page-request-fulfill.spec.ts index 90cc071c8e..4c6b4a830c 100644 --- a/tests/page/page-request-fulfill.spec.ts +++ b/tests/page/page-request-fulfill.spec.ts @@ -248,3 +248,54 @@ it('should fetch original request and fulfill', async ({ page, server, isElectro expect(response.status()).toBe(200); expect(await page.title()).toEqual('Woof-Woof'); }); + +it('should fulfill with multiple set-cookie', async ({ page, server, browserName }) => { + it.fail(browserName === 'webkit', 'Response contained invalid HTTP headers'); + const cookies = ['a=b', 'c=d']; + await page.route('**/empty.html', async route => { + route.fulfill({ + status: 200, + headers: { + 'X-Header-1': 'v1', + 'Set-Cookie': cookies.join('\n'), + 'X-Header-2': 'v2', + }, + body: '' + }); + }); + const response = await page.goto(server.EMPTY_PAGE); + expect((await page.evaluate(() => document.cookie)).split(';').map(s => s.trim()).sort()).toEqual(cookies); + expect(await response.headerValue('X-Header-1')).toBe('v1'); + expect(await response.headerValue('X-Header-2')).toBe('v2'); +}); + +it('should fulfill with fetch response that has multiple set-cookie', async ({ playwright, page, server, browserName }) => { + it.fail(browserName === 'webkit', 'Response contained invalid HTTP headers'); + server.setRoute('/empty.html', (req, res) => { + res.setHeader('Set-Cookie', ['a=b', 'c=d']); + res.end(); + }); + await page.route('**/empty.html', async route => { + const request = await playwright._newRequest(); + const response = await request.fetch(route.request()); + route.fulfill({ response }); + }); + await page.goto(server.EMPTY_PAGE); + const cookie = await page.evaluate(() => document.cookie); + expect(cookie.split(';').map(s => s.trim()).sort()).toEqual(['a=b', 'c=d']); +}); + +it('headerValue should return set-cookie from intercepted response', async ({ page, server, browserName }) => { + it.fail(browserName === 'chromium', 'Set-Cookie is missing in response after interception'); + await page.route('**/empty.html', async route => { + route.fulfill({ + status: 200, + headers: { + 'Set-Cookie': 'a=b', + }, + body: '' + }); + }); + const response = await page.goto(server.EMPTY_PAGE); + expect(await response.headerValue('Set-Cookie')).toBe('a=b'); +});