From 09e97afd223544b0dff3fdd1d5adce2d6d3c1479 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Tue, 28 Jan 2020 14:29:46 -0800 Subject: [PATCH] feat(wk,ff): amend method & postData upon continue (#703) Fixes #668 --- docs/api.md | 2 ++ package.json | 2 +- src/chromium/crNetworkManager.ts | 4 ++- src/firefox/ffNetworkManager.ts | 6 ++++- src/network.ts | 4 +-- src/webkit/wkInterceptableRequest.ts | 8 +++--- test/interception.spec.js | 39 ++++++++++++++++++++++++++-- 7 files changed, 55 insertions(+), 10 deletions(-) diff --git a/docs/api.md b/docs/api.md index 5d5e5aac488ae..2b5044381152b 100644 --- a/docs/api.md +++ b/docs/api.md @@ -2849,6 +2849,8 @@ Exception is immediately thrown if the request interception is not enabled. #### request.continue([overrides]) - `overrides` <[Object]> Optional request overwrites, 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 - `headers` <[Object]> If set changes the request HTTP headers. Header values will be converted to a string. - returns: <[Promise]> diff --git a/package.json b/package.json index e24ec65dbc20e..74984b65a53d6 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "main": "index.js", "playwright": { "chromium_revision": "733125", - "firefox_revision": "1018", + "firefox_revision": "1019", "webkit_revision": "1119" }, "scripts": { diff --git a/src/chromium/crNetworkManager.ts b/src/chromium/crNetworkManager.ts index 2853f7c027027..fad536e1ab098 100644 --- a/src/chromium/crNetworkManager.ts +++ b/src/chromium/crNetworkManager.ts @@ -279,10 +279,12 @@ class InterceptableRequest implements network.RequestDelegate { event.request.url, (event.type || '').toLowerCase(), event.request.method, event.request.postData, headersObject(event.request.headers)); } - async continue(overrides: { headers?: network.Headers; } = {}) { + async continue(overrides: { method?: string; headers?: network.Headers; postData?: string } = {}) { await this._client.send('Fetch.continueRequest', { requestId: this._interceptionId!, headers: overrides.headers ? headersArray(overrides.headers) : undefined, + method: overrides.method, + postData: overrides.postData }).catch(error => { // In certain cases, protocol will return error if the request was already canceled // or the page was closed. We should tolerate these errors. diff --git a/src/firefox/ffNetworkManager.ts b/src/firefox/ffNetworkManager.ts index c45b196af646f..c513622c29043 100644 --- a/src/firefox/ffNetworkManager.ts +++ b/src/firefox/ffNetworkManager.ts @@ -158,13 +158,17 @@ class InterceptableRequest implements network.RequestDelegate { payload.url, causeToResourceType[payload.cause] || 'other', payload.method, payload.postData, headers); } - async continue(overrides: { headers?: { [key: string]: string } } = {}) { + async continue(overrides: { method?: string; headers?: network.Headers; postData?: string }) { const { + method, headers, + postData } = overrides; await this._session.send('Network.resumeInterceptedRequest', { requestId: this._id, + method, headers: headers ? headersArray(headers) : undefined, + postData: postData ? Buffer.from(postData).toString('base64') : undefined }).catch(error => { debugError(error); }); diff --git a/src/network.ts b/src/network.ts index 1f663dd436c37..e776f20c97467 100644 --- a/src/network.ts +++ b/src/network.ts @@ -211,7 +211,7 @@ export class Request { await this._delegate!.fulfill(response); } - async continue(overrides: { headers?: { [key: string]: string } } = {}) { + async continue(overrides: { method?: string; headers?: Headers; postData?: string } = {}) { assert(this._delegate, 'Request Interception is not enabled!'); assert(!this._interceptionHandled, 'Request is already handled!'); await this._delegate!.continue(overrides); @@ -301,7 +301,7 @@ export class Response { export interface RequestDelegate { abort(errorCode: string): Promise; fulfill(response: { status: number; headers: Headers; contentType: string; body: (string | platform.BufferType); }): Promise; - continue(overrides: { url?: string; method?: string; postData?: string; headers?: Headers; }): Promise; + continue(overrides: { method?: string; headers?: Headers; postData?: string; }): Promise; } export class WebSocket extends platform.EventEmitter { diff --git a/src/webkit/wkInterceptableRequest.ts b/src/webkit/wkInterceptableRequest.ts index a860da39dfeee..476306e99b30e 100644 --- a/src/webkit/wkInterceptableRequest.ts +++ b/src/webkit/wkInterceptableRequest.ts @@ -96,12 +96,14 @@ export class WKInterceptableRequest implements network.RequestDelegate { }); } - async continue(overrides: { headers?: { [key: string]: string; }; }) { + async continue(overrides: { method?: string; headers?: network.Headers; postData?: string }) { await this._interceptedPromise; await this._session.send('Network.interceptContinue', { requestId: this._requestId, - ...overrides - }).catch(error => { + method: overrides.method, + headers: overrides.headers, + postData: overrides.postData ? Buffer.from(overrides.postData).toString('base64') : undefined + }).catch((error: Error) => { // In certain cases, protocol will return error if the request was already canceled // or the page was closed. We should tolerate these errors. debugError(error); diff --git a/test/interception.spec.js b/test/interception.spec.js index 5195620f8ccba..989a4569a78ca 100644 --- a/test/interception.spec.js +++ b/test/interception.spec.js @@ -416,7 +416,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p }); }); - describe('Interception.continue', function() { + describe('Request.continue', function() { it('should work', async({page, server}) => { await page.setRequestInterception(true); page.on('request', request => request.continue()); @@ -436,9 +436,44 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p ]); expect(request.headers['foo']).toBe('bar'); }); + it('should amend method', async({page, server}) => { + const sRequest = server.waitForRequest('/sleep.zzz'); + await page.goto(server.EMPTY_PAGE); + await page.setRequestInterception(true); + page.on('request', request => { + request.continue({ method: 'POST' }); + }); + const [request] = await Promise.all([ + server.waitForRequest('/sleep.zzz'), + page.evaluate(() => fetch('/sleep.zzz')) + ]); + expect(request.method).toBe('POST'); + expect((await sRequest).method).toBe('POST'); + }); + it('should amend method on main request', async({page, server}) => { + const request = server.waitForRequest('/empty.html'); + await page.setRequestInterception(true); + page.on('request', request => { + request.continue({ method: 'POST' }); + }); + await page.goto(server.EMPTY_PAGE); + expect((await request).method).toBe('POST'); + }); + it('should amend post data', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + await page.setRequestInterception(true); + page.on('request', request => { + request.continue({ postData: 'doggo' }); + }); + const [serverRequest] = await Promise.all([ + server.waitForRequest('/sleep.zzz'), + page.evaluate(() => fetch('/sleep.zzz', { method: 'POST', body: 'birdy' })) + ]); + expect(await serverRequest.postBody).toBe('doggo'); + }); }); - describe('interception.fulfill', function() { + describe('Request.fulfill', function() { it('should work', async({page, server}) => { await page.setRequestInterception(true); page.on('request', request => {