From 3a7d629c61078a1cf8f4fdc5e666cc7765b38af0 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Mon, 29 Jun 2020 16:37:38 -0700 Subject: [PATCH] chore(rpc): pass more network tests (#2762) --- src/chromium/crNetworkManager.ts | 12 +++++------- src/firefox/ffNetworkManager.ts | 12 +++++------- src/network.ts | 16 +++------------- src/rpc/channels.ts | 15 ++++++++++++--- src/rpc/client/browser.ts | 1 + src/rpc/client/browserContext.ts | 3 +++ src/rpc/client/jsHandle.ts | 2 +- src/rpc/client/network.ts | 9 ++++++++- src/rpc/client/page.ts | 1 + src/rpc/serializers.ts | 17 +++++++++++++++++ src/rpc/server/jsHandleDispatcher.ts | 4 ++-- src/rpc/server/networkDispatchers.ts | 12 +++++++++--- src/types.ts | 7 +++++++ src/webkit/wkInterceptableRequest.ts | 12 +++++------- 14 files changed, 79 insertions(+), 44 deletions(-) diff --git a/src/chromium/crNetworkManager.ts b/src/chromium/crNetworkManager.ts index 1fca1ffac0..2f7ad5cb49 100644 --- a/src/chromium/crNetworkManager.ts +++ b/src/chromium/crNetworkManager.ts @@ -361,14 +361,12 @@ class InterceptableRequest implements network.RouteDelegate { }); } - async fulfill(response: types.FulfillResponse) { + async fulfill(response: types.NormalizedFulfillResponse) { const responseBody = response.body && helper.isString(response.body) ? Buffer.from(response.body) : (response.body || null); const responseHeaders: { [s: string]: string; } = {}; - if (response.headers) { - for (const header of Object.keys(response.headers)) - responseHeaders[header.toLowerCase()] = response.headers[header]; - } + for (const header of Object.keys(response.headers)) + responseHeaders[header.toLowerCase()] = response.headers[header]; if (response.contentType) responseHeaders['content-type'] = response.contentType; if (responseBody && !('content-length' in responseHeaders)) @@ -378,8 +376,8 @@ class InterceptableRequest implements network.RouteDelegate { // or the page was closed. We should tolerate these errors. await this._client._sendMayFail('Fetch.fulfillRequest', { requestId: this._interceptionId!, - responseCode: response.status || 200, - responsePhrase: network.STATUS_TEXTS[String(response.status || 200)], + responseCode: response.status, + responsePhrase: network.STATUS_TEXTS[String(response.status)], responseHeaders: headersArray(responseHeaders), body: responseBody ? responseBody.toString('base64') : undefined, }); diff --git a/src/firefox/ffNetworkManager.ts b/src/firefox/ffNetworkManager.ts index a3c8e58d95..9b25ea5d1f 100644 --- a/src/firefox/ffNetworkManager.ts +++ b/src/firefox/ffNetworkManager.ts @@ -172,14 +172,12 @@ class InterceptableRequest implements network.RouteDelegate { }); } - async fulfill(response: types.FulfillResponse) { + async fulfill(response: types.NormalizedFulfillResponse) { const responseBody = response.body && helper.isString(response.body) ? Buffer.from(response.body) : (response.body || null); const responseHeaders: { [s: string]: string; } = {}; - if (response.headers) { - for (const header of Object.keys(response.headers)) - responseHeaders[header.toLowerCase()] = response.headers[header]; - } + for (const header of Object.keys(response.headers)) + responseHeaders[header.toLowerCase()] = response.headers[header]; if (response.contentType) responseHeaders['content-type'] = response.contentType; if (responseBody && !('content-length' in responseHeaders)) @@ -187,8 +185,8 @@ class InterceptableRequest implements network.RouteDelegate { await this._session.sendMayFail('Network.fulfillInterceptedRequest', { requestId: this._id, - status: response.status || 200, - statusText: network.STATUS_TEXTS[String(response.status || 200)] || '', + status: response.status, + statusText: network.STATUS_TEXTS[String(response.status)] || '', headers: headersArray(responseHeaders), base64body: responseBody ? responseBody.toString('base64') : undefined, }); diff --git a/src/network.ts b/src/network.ts index 29436552b0..e1f858a6e9 100644 --- a/src/network.ts +++ b/src/network.ts @@ -14,13 +14,11 @@ * limitations under the License. */ -import * as fs from 'fs'; -import * as mime from 'mime'; -import * as util from 'util'; import * as frames from './frames'; import * as types from './types'; import { assert, helper } from './helper'; import { URLSearchParams } from 'url'; +import { normalizeFulfillParameters } from './rpc/serializers'; export function filterCookies(cookies: types.NetworkCookie[], urls: string[]): types.NetworkCookie[] { const parsedURLs = urls.map(s => new URL(s)); @@ -220,15 +218,7 @@ export class Route { async fulfill(response: types.FulfillResponse & { path?: string }) { assert(!this._handled, 'Route is already handled!'); this._handled = true; - if (response.path) { - response = { - status: response.status, - headers: response.headers, - contentType: mime.getType(response.path) || 'application/octet-stream', - body: await util.promisify(fs.readFile)(response.path) - }; - } - await this._delegate.fulfill(response); + await this._delegate.fulfill(await normalizeFulfillParameters(response)); } async continue(overrides: { method?: string; headers?: types.Headers; postData?: string } = {}) { @@ -325,7 +315,7 @@ export class Response { export interface RouteDelegate { abort(errorCode: string): Promise; - fulfill(response: types.FulfillResponse): Promise; + fulfill(response: types.NormalizedFulfillResponse): Promise; continue(overrides: { method?: string; headers?: types.Headers; postData?: string; }): Promise; } diff --git a/src/rpc/channels.ts b/src/rpc/channels.ts index 1ae4b09aba..bccca4bb3f 100644 --- a/src/rpc/channels.ts +++ b/src/rpc/channels.ts @@ -17,6 +17,8 @@ import { EventEmitter } from 'events'; import * as types from '../types'; +export type Binary = string; + export interface Channel extends EventEmitter { _type: string; _guid: string; @@ -101,7 +103,7 @@ export interface PageChannel extends Channel { goForward(params: { options?: types.NavigateOptions }): Promise; opener(): Promise; reload(params: { options?: types.NavigateOptions }): Promise; - screenshot(params: { options?: types.ScreenshotOptions }): Promise; + screenshot(params: { options?: types.ScreenshotOptions }): Promise; setExtraHTTPHeaders(params: { headers: types.Headers }): Promise; setNetworkInterceptionEnabled(params: { enabled: boolean }): Promise; setViewportSize(params: { viewportSize: types.Size }): Promise; @@ -199,7 +201,7 @@ export interface ElementHandleChannel extends JSHandleChannel { press(params: { key: string; options?: { delay?: number } & types.TimeoutOptions & { noWaitAfter?: boolean } }): Promise; querySelector(params: { selector: string }): Promise; querySelectorAll(params: { selector: string }): Promise; - screenshot(params: { options?: types.ElementScreenshotOptions }): Promise; + screenshot(params: { options?: types.ElementScreenshotOptions }): Promise; scrollIntoViewIfNeeded(params: { options?: types.TimeoutOptions }): Promise; selectOption(params: { values: string | ElementHandleChannel | types.SelectOption | string[] | ElementHandleChannel[] | types.SelectOption[] | null; options?: types.NavigatingActionWaitOptions }): string[] | Promise; selectText(params: { options?: types.TimeoutOptions }): Promise; @@ -228,7 +230,14 @@ export type RequestInitializer = { export interface RouteChannel extends Channel { abort(params: { errorCode: string }): Promise; continue(params: { overrides: { method?: string, headers?: types.Headers, postData?: string } }): Promise; - fulfill(params: { response: types.FulfillResponse & { path?: string } }): Promise; + fulfill(params: { + response: { + status?: number, + headers?: types.Headers, + contentType?: string, + body: Binary, + } + }): Promise; } export type RouteInitializer = { request: RequestChannel, diff --git a/src/rpc/client/browser.ts b/src/rpc/client/browser.ts index 3b7994fa74..3c43250c89 100644 --- a/src/rpc/client/browser.ts +++ b/src/rpc/client/browser.ts @@ -55,6 +55,7 @@ export class Browser extends ChannelOwner { const context = await this.newContext(options); const page = await context.newPage(); page._ownedContext = context; + context._ownerPage = page; return page; } diff --git a/src/rpc/client/browserContext.ts b/src/rpc/client/browserContext.ts index 37c98f37d6..663e7e4237 100644 --- a/src/rpc/client/browserContext.ts +++ b/src/rpc/client/browserContext.ts @@ -34,6 +34,7 @@ export class BrowserContext extends ChannelOwner(); private _pendingWaitForEvents = new Map<(error: Error) => void, string>(); _timeoutSettings = new TimeoutSettings(); + _ownerPage: Page | undefined; static from(context: BrowserContextChannel): BrowserContext { return context._object; @@ -91,6 +92,8 @@ export class BrowserContext extends ChannelOwner { + if (this._ownerPage) + throw new Error('Please use browser.newContext()'); return Page.from(await this._channel.newPage()); } diff --git a/src/rpc/client/jsHandle.ts b/src/rpc/client/jsHandle.ts index 84db879be0..abadc55d2b 100644 --- a/src/rpc/client/jsHandle.ts +++ b/src/rpc/client/jsHandle.ts @@ -82,7 +82,7 @@ export class JSHandle extends ChannelOwner { - return await this._channel.jsonValue(); + return parseResult(await this._channel.jsonValue()); } asElement(): ElementHandle | null { diff --git a/src/rpc/client/network.ts b/src/rpc/client/network.ts index 08ee61907a..927797b2f5 100644 --- a/src/rpc/client/network.ts +++ b/src/rpc/client/network.ts @@ -20,6 +20,7 @@ import { RequestChannel, ResponseChannel, RouteChannel, RequestInitializer, Resp import { ChannelOwner } from './channelOwner'; import { Frame } from './frame'; import { Connection } from '../connection'; +import { normalizeFulfillParameters } from '../serializers'; export type NetworkCookie = { name: string, @@ -150,7 +151,13 @@ export class Route extends ChannelOwner { } async fulfill(response: types.FulfillResponse & { path?: string }) { - await this._channel.fulfill({ response }); + const normalized = await normalizeFulfillParameters(response); + await this._channel.fulfill({ response: { + status: normalized.status, + headers: normalized.headers, + contentType: normalized.contentType, + body: (typeof normalized.body === 'string' ? Buffer.from(normalized.body) : normalized.body).toString('base64') + }}); } async continue(overrides: { method?: string; headers?: types.Headers; postData?: string } = {}) { diff --git a/src/rpc/client/page.ts b/src/rpc/client/page.ts index bf4a88cf5c..a0c02e0da4 100644 --- a/src/rpc/client/page.ts +++ b/src/rpc/client/page.ts @@ -334,6 +334,7 @@ export class Page extends ChannelOwner { } async setViewportSize(viewportSize: types.Size) { + this._viewportSize = viewportSize; await this._channel.setViewportSize({ viewportSize }); } diff --git a/src/rpc/serializers.ts b/src/rpc/serializers.ts index aa5daf4aec..add2185f40 100644 --- a/src/rpc/serializers.ts +++ b/src/rpc/serializers.ts @@ -62,3 +62,20 @@ export async function normalizeFilePayloads(files: string | types.FilePayload | } return filePayloads; } + +export async function normalizeFulfillParameters(params: types.FulfillResponse & { path?: string }): Promise { + if (params.path) { + return { + status: params.status || 200, + headers: params.headers || {}, + contentType: mime.getType(params.path) || 'application/octet-stream', + body: await util.promisify(fs.readFile)(params.path) + }; + } + return { + status: params.status || 200, + headers: params.headers || {}, + contentType: params.contentType, + body: params.body || '' + }; +} diff --git a/src/rpc/server/jsHandleDispatcher.ts b/src/rpc/server/jsHandleDispatcher.ts index 18c9cea193..094af5c27f 100644 --- a/src/rpc/server/jsHandleDispatcher.ts +++ b/src/rpc/server/jsHandleDispatcher.ts @@ -46,7 +46,7 @@ export class JSHandleDispatcher extends Dispatcher { - return this._object.jsonValue(); + return serializeResult(await this._object.jsonValue()); } async dispose() { @@ -55,7 +55,7 @@ export class JSHandleDispatcher extends Dispatcher impleme await this._object.continue(params.overrides); } - async fulfill(params: { response: types.FulfillResponse & { path?: string } }): Promise { - await this._object.fulfill(params.response); + async fulfill(params: { response: { status?: number, headers?: types.Headers, contentType?: string, body: Binary } }): Promise { + const { response } = params; + await this._object.fulfill({ + status: response.status, + headers: response.headers, + contentType: response.contentType, + body: Buffer.from(response.body, 'base64'), + }); } async abort(params: { errorCode: string }): Promise { diff --git a/src/types.ts b/src/types.ts index 5445dac061..48e366f3b3 100644 --- a/src/types.ts +++ b/src/types.ts @@ -196,6 +196,13 @@ export type FulfillResponse = { body?: string | Buffer, }; +export type NormalizedFulfillResponse = { + status: number, + headers: Headers, + contentType?: string, + body: string | Buffer, +}; + export type NetworkCookie = { name: string, value: string, diff --git a/src/webkit/wkInterceptableRequest.ts b/src/webkit/wkInterceptableRequest.ts index 5695e1404f..f0fc3f7fcc 100644 --- a/src/webkit/wkInterceptableRequest.ts +++ b/src/webkit/wkInterceptableRequest.ts @@ -66,17 +66,15 @@ export class WKInterceptableRequest implements network.RouteDelegate { await this._session.sendMayFail('Network.interceptRequestWithError', { requestId: this._requestId, errorType }); } - async fulfill(response: types.FulfillResponse) { + async fulfill(response: types.NormalizedFulfillResponse) { await this._interceptedPromise; const base64Encoded = !!response.body && !helper.isString(response.body); const responseBody = response.body ? (base64Encoded ? response.body.toString('base64') : response.body as string) : ''; const responseHeaders: { [s: string]: string; } = {}; - if (response.headers) { - for (const header of Object.keys(response.headers)) - responseHeaders[header.toLowerCase()] = String(response.headers[header]); - } + for (const header of Object.keys(response.headers)) + responseHeaders[header.toLowerCase()] = String(response.headers[header]); let mimeType = base64Encoded ? 'application/octet-stream' : 'text/plain'; if (response.contentType) { responseHeaders['content-type'] = response.contentType; @@ -93,8 +91,8 @@ export class WKInterceptableRequest implements network.RouteDelegate { // or the page was closed. We should tolerate these errors. await this._session.sendMayFail('Network.interceptRequestWithResponse', { requestId: this._requestId, - status: response.status || 200, - statusText: network.STATUS_TEXTS[String(response.status || 200)], + status: response.status, + statusText: network.STATUS_TEXTS[String(response.status)], mimeType, headers: responseHeaders, base64Encoded,