diff --git a/README.md b/README.md index 8be8ff93b0..9e803643ec 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 🎭 Playwright -[![npm version](https://img.shields.io/npm/v/playwright.svg?style=flat)](https://www.npmjs.com/package/playwright) [![Join Slack](https://img.shields.io/badge/join-slack-infomational)](https://join.slack.com/t/playwright/shared_invite/enQtOTEyMTUxMzgxMjIwLThjMDUxZmIyNTRiMTJjNjIyMzdmZDA3MTQxZWUwZTFjZjQwNGYxZGM5MzRmNzZlMWI5ZWUyOTkzMjE5Njg1NDg) [![Chromium version](https://img.shields.io/badge/chromium-86.0.4209.0-blue.svg?logo=google-chrome)](https://www.chromium.org/Home) [![Firefox version](https://img.shields.io/badge/firefox-78.0b5-blue.svg?logo=mozilla-firefox)](https://www.mozilla.org/en-US/firefox/new/) [![WebKit version](https://img.shields.io/badge/webkit-14.0-blue.svg?logo=safari)](https://webkit.org/) +[![npm version](https://img.shields.io/npm/v/playwright.svg?style=flat)](https://www.npmjs.com/package/playwright) [![Join Slack](https://img.shields.io/badge/join-slack-infomational)](https://join.slack.com/t/playwright/shared_invite/enQtOTEyMTUxMzgxMjIwLThjMDUxZmIyNTRiMTJjNjIyMzdmZDA3MTQxZWUwZTFjZjQwNGYxZGM5MzRmNzZlMWI5ZWUyOTkzMjE5Njg1NDg) [![Chromium version](https://img.shields.io/badge/chromium-86.0.4211.0-blue.svg?logo=google-chrome)](https://www.chromium.org/Home) [![Firefox version](https://img.shields.io/badge/firefox-78.0b5-blue.svg?logo=mozilla-firefox)](https://www.mozilla.org/en-US/firefox/new/) [![WebKit version](https://img.shields.io/badge/webkit-14.0-blue.svg?logo=safari)](https://webkit.org/) ##### [Docs](docs/README.md) | [API reference](docs/api.md) | [Changelog](https://github.com/microsoft/playwright/releases) @@ -8,7 +8,7 @@ Playwright is a Node library to automate [Chromium](https://www.chromium.org/Hom | | Linux | macOS | Windows | | :--- | :---: | :---: | :---: | -| Chromium 86.0.4209.0 | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| Chromium 86.0.4211.0 | :white_check_mark: | :white_check_mark: | :white_check_mark: | | WebKit 14.0 | :white_check_mark: | :white_check_mark: | :white_check_mark: | | Firefox 78.0b5 | :white_check_mark: | :white_check_mark: | :white_check_mark: | diff --git a/browsers.json b/browsers.json index 3c77001012..46b0ad902b 100644 --- a/browsers.json +++ b/browsers.json @@ -3,7 +3,7 @@ "browsers": [ { "name": "chromium", - "revision": "790602" + "revision": "791201" }, { "name": "firefox", diff --git a/docs/api.md b/docs/api.md index f21b3b7a7c..a8bc9ba5fe 100644 --- a/docs/api.md +++ b/docs/api.md @@ -3729,7 +3729,7 @@ Aborts the route's request. #### route.continue([overrides]) - `overrides` <[Object]> Optional request overrides, which can be one of the following: - `method` <[string]> If set changes the request method (e.g. GET or POST) - - `postData` <[string]> If set changes the post data of request + - `postData` <[string]|[Buffer]> If set changes the post data of request - `headers` <[Object]<[string], [string]>> If set changes the request HTTP headers. Header values will be converted to a string. - returns: <[Promise]> diff --git a/src/chromium/crNetworkManager.ts b/src/chromium/crNetworkManager.ts index 9f2a22ea15..a7ad83d6f6 100644 --- a/src/chromium/crNetworkManager.ts +++ b/src/chromium/crNetworkManager.ts @@ -359,7 +359,7 @@ class InterceptableRequest implements network.RouteDelegate { requestId: this._interceptionId!, headers: overrides.headers, method: overrides.method, - postData: overrides.postData + postData: overrides.postData ? overrides.postData.toString('base64') : undefined }); } diff --git a/src/chromium/protocol.ts b/src/chromium/protocol.ts index 9cb92d023a..f3ef64e516 100644 --- a/src/chromium/protocol.ts +++ b/src/chromium/protocol.ts @@ -11856,7 +11856,7 @@ If absent, a standard phrase matching responseCode is used. /** * If set, overrides the post data in the request. */ - postData?: string; + postData?: binary; /** * If set, overrides the request headers. */ diff --git a/src/rpc/client/network.ts b/src/rpc/client/network.ts index c70b2a57ea..9159f1456d 100644 --- a/src/rpc/client/network.ts +++ b/src/rpc/client/network.ts @@ -168,7 +168,12 @@ export class Route extends ChannelOwner { } async continue(overrides: types.ContinueOverrides = {}) { - await this._channel.continue(normalizeContinueOverrides(overrides)); + const normalized = normalizeContinueOverrides(overrides); + await this._channel.continue({ + method: normalized.method, + headers: normalized.headers, + postData: normalized.postData ? normalized.postData.toString('base64') : undefined + }); } } diff --git a/src/rpc/protocol.yml b/src/rpc/protocol.yml index 1646ebc174..8c4222e105 100644 --- a/src/rpc/protocol.yml +++ b/src/rpc/protocol.yml @@ -1686,7 +1686,7 @@ Route: properties: name: string value: string - postData: string? + postData: binary? fulfill: parameters: diff --git a/src/rpc/serializers.ts b/src/rpc/serializers.ts index b6702c9066..f255c54ddc 100644 --- a/src/rpc/serializers.ts +++ b/src/rpc/serializers.ts @@ -107,7 +107,7 @@ export function normalizeContinueOverrides(overrides: types.ContinueOverrides): return { method: overrides.method, headers: overrides.headers ? headersObjectToArray(overrides.headers) : undefined, - postData: overrides.postData, + postData: helper.isString(overrides.postData) ? Buffer.from(overrides.postData, 'utf8') : overrides.postData, }; } diff --git a/src/rpc/server/networkDispatchers.ts b/src/rpc/server/networkDispatchers.ts index 37e728d34f..0f466c536a 100644 --- a/src/rpc/server/networkDispatchers.ts +++ b/src/rpc/server/networkDispatchers.ts @@ -82,11 +82,11 @@ export class RouteDispatcher extends Dispatcher impleme }); } - async continue(params: types.NormalizedContinueOverrides): Promise { + async continue(params: { method?: string, headers?: types.HeadersArray, postData?: string }): Promise { await this._object.continue({ method: params.method, headers: params.headers ? headersArrayToObject(params.headers) : undefined, - postData: params.postData, + postData: params.postData ? Buffer.from(params.postData, 'base64') : undefined, }); } diff --git a/src/types.ts b/src/types.ts index e51baf0c3a..3b532e0a1c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -227,13 +227,13 @@ export type NormalizedFulfillResponse = { export type ContinueOverrides = { method?: string, headers?: Headers, - postData?: string, + postData?: string | Buffer, }; export type NormalizedContinueOverrides = { method?: string, headers?: HeadersArray, - postData?: string, + postData?: Buffer, }; export type NetworkCookie = { diff --git a/test/interception.jest.js b/test/interception.jest.js index 11860f4d34..6ad423dda9 100644 --- a/test/interception.jest.js +++ b/test/interception.jest.js @@ -532,7 +532,18 @@ describe('Request.continue', function() { server.waitForRequest('/sleep.zzz'), page.evaluate(() => fetch('/sleep.zzz', { method: 'POST', body: 'birdy' })) ]); - expect(await serverRequest.postBody).toBe('doggo'); + expect((await serverRequest.postBody).toString('utf8')).toBe('doggo'); + }); + it.fail(FFOX)('should amend utf8 post data', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + await page.route('**/*', route => { + route.continue({ postData: 'пушкин' }); + }); + const [serverRequest] = await Promise.all([ + server.waitForRequest('/sleep.zzz'), + page.evaluate(() => fetch('/sleep.zzz', { method: 'POST', body: 'birdy' })) + ]); + expect((await serverRequest.postBody).toString('utf8')).toBe('пушкин'); }); it.fail(FFOX)('should amend longer post data', async({page, server}) => { await page.goto(server.EMPTY_PAGE); @@ -543,7 +554,22 @@ describe('Request.continue', function() { server.waitForRequest('/sleep.zzz'), page.evaluate(() => fetch('/sleep.zzz', { method: 'POST', body: 'birdy' })) ]); - expect(await serverRequest.postBody).toBe('doggo-is-longer-than-birdy'); + expect((await serverRequest.postBody).toString('utf8')).toBe('doggo-is-longer-than-birdy'); + }); + it.fail(FFOX)('should amend binary post data', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + const arr = Array.from(Array(256).keys()); + await page.route('**/*', route => { + route.continue({ postData: Buffer.from(arr) }); + }); + const [serverRequest] = await Promise.all([ + server.waitForRequest('/sleep.zzz'), + page.evaluate(() => fetch('/sleep.zzz', { method: 'POST', body: 'birdy' })) + ]); + const buffer = await serverRequest.postBody; + expect(buffer.length).toBe(arr.length); + for (let i = 0; i < arr.length; ++i) + expect(arr[i]).toBe(buffer[i]); }); }); diff --git a/utils/testserver/index.js b/utils/testserver/index.js index 5d901dcc67..fcd2924142 100644 --- a/utils/testserver/index.js +++ b/utils/testserver/index.js @@ -194,8 +194,8 @@ class TestServer { throw error; }); request.postBody = new Promise(resolve => { - let body = ''; - request.on('data', chunk => body += chunk); + let body = Buffer.from([]); + request.on('data', chunk => body = Buffer.concat([body, chunk])); request.on('end', () => resolve(body)); }); const pathName = url.parse(request.url).path;