From 59df541de5ed5c8214bc49dffb66abda1ca44a13 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Wed, 11 Aug 2021 14:47:05 -0700 Subject: [PATCH] fix: fulfill intercepted response with empty body (#8151) --- src/client/network.ts | 2 +- src/server/firefox/ffNetworkManager.ts | 17 ++++++++++++----- src/server/network.ts | 8 ++++---- tests/page/page-request-intercept.spec.ts | 15 +++++++++++++++ 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/client/network.ts b/src/client/network.ts index e7236b28f8..74cdc566a3 100644 --- a/src/client/network.ts +++ b/src/client/network.ts @@ -265,7 +265,7 @@ export class Route extends ChannelOwner { - let body = ''; + let body = undefined; let isBase64 = false; let length = 0; if (options.path) { diff --git a/src/server/firefox/ffNetworkManager.ts b/src/server/firefox/ffNetworkManager.ts index 265c902b2c..eddc7e7788 100644 --- a/src/server/firefox/ffNetworkManager.ts +++ b/src/server/firefox/ffNetworkManager.ts @@ -179,15 +179,25 @@ const internalCauseToResourceType: {[key: string]: string} = { class InterceptableRequest { readonly request: network.Request; readonly _id: string; + private _redirectedTo: InterceptableRequest | undefined; constructor(frame: frames.Frame, redirectedFrom: InterceptableRequest | null, payload: Protocol.Network.requestWillBeSentPayload) { this._id = payload.requestId; + if (redirectedFrom) + redirectedFrom._redirectedTo = this; let postDataBuffer = null; if (payload.postData) postDataBuffer = Buffer.from(payload.postData, 'base64'); this.request = new network.Request(frame, redirectedFrom ? redirectedFrom.request : null, payload.navigationId, payload.url, internalCauseToResourceType[payload.internalCause] || causeToResourceType[payload.cause] || 'other', payload.method, postDataBuffer, payload.headers); } + + _finalRequest(): InterceptableRequest { + let request: InterceptableRequest = this; + while (request._redirectedTo) + request = request._redirectedTo; + return request; + } } class FFRouteImpl implements network.RouteDelegate { @@ -199,12 +209,9 @@ class FFRouteImpl implements network.RouteDelegate { this._request = request; } - async responseBody(forFulfill: boolean): Promise { - // Empty buffer will result in the response being used. - if (forFulfill) - return Buffer.from(''); + async responseBody(): Promise { const response = await this._session.send('Network.getResponseBody', { - requestId: this._request._id + requestId: this._request._finalRequest()._id }); return Buffer.from(response.base64body, 'base64'); } diff --git a/src/server/network.ts b/src/server/network.ts index 26a43dc9e3..a306e564bd 100644 --- a/src/server/network.ts +++ b/src/server/network.ts @@ -222,9 +222,9 @@ export class Route extends SdkObject { this._handled = true; let body = overrides.body; let isBase64 = overrides.isBase64 || false; - if (!body) { + if (body === undefined) { if (this._response) { - body = (await this._delegate.responseBody(true)).toString('utf8'); + body = (await this._delegate.responseBody()).toString('utf8'); isBase64 = false; } else { body = ''; @@ -254,7 +254,7 @@ export class Route extends SdkObject { async responseBody(): Promise { assert(!this._handled, 'Route is already handled!'); - return this._delegate.responseBody(false); + return this._delegate.responseBody(); } } @@ -474,7 +474,7 @@ export interface RouteDelegate { abort(errorCode: string): Promise; fulfill(response: types.NormalizedFulfillResponse): Promise; continue(request: Request, overrides: types.NormalizedContinueOverrides): Promise; - responseBody(forFulfill: boolean): Promise; + responseBody(): Promise; } // List taken from https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml with extra 306 and 418 codes. diff --git a/tests/page/page-request-intercept.spec.ts b/tests/page/page-request-intercept.spec.ts index b9580602f7..8231e60735 100644 --- a/tests/page/page-request-intercept.spec.ts +++ b/tests/page/page-request-intercept.spec.ts @@ -40,6 +40,21 @@ it('should fulfill intercepted response', async ({page, server, browserName}) => expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!'); }); +it('should fulfill response with empty body', async ({page, server, browserName}) => { + it.fail(browserName === 'firefox'); + await page.route('**/*', async route => { + // @ts-expect-error + await route._intercept({}); + await route.fulfill({ + status: 201, + body: '' + }); + }); + const response = await page.goto(server.PREFIX + '/title.html'); + expect(response.status()).toBe(201); + expect(await response.text()).toBe(''); +}); + it('should throw on continue after intercept', async ({page, server, browserName}) => { let routeCallback; const routePromise = new Promise(f => routeCallback = f);