chore: remove route/unroute from the server side (#3518)

We only use a global "intercept all requests" handler on
page and browser context, instead of granular ones.
This commit is contained in:
Dmitry Gozman 2020-08-18 17:34:04 -07:00 committed by GitHub
parent 3cf48f9bd4
commit 20c6b85178
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 27 additions and 67 deletions

View file

@ -44,8 +44,7 @@ export interface BrowserContext extends EventEmitter {
setHTTPCredentials(httpCredentials?: types.Credentials): Promise<void>; setHTTPCredentials(httpCredentials?: types.Credentials): Promise<void>;
addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any): Promise<void>; addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any): Promise<void>;
exposeBinding(name: string, playwrightBinding: frames.FunctionWithSource): Promise<void>; exposeBinding(name: string, playwrightBinding: frames.FunctionWithSource): Promise<void>;
route(url: types.URLMatch, handler: network.RouteHandler): Promise<void>; _setRequestInterceptor(handler: network.RouteHandler | undefined): Promise<void>;
unroute(url: types.URLMatch, handler?: network.RouteHandler): Promise<void>;
close(): Promise<void>; close(): Promise<void>;
} }
@ -53,7 +52,7 @@ export abstract class BrowserContextBase extends EventEmitter implements Browser
readonly _timeoutSettings = new TimeoutSettings(); readonly _timeoutSettings = new TimeoutSettings();
readonly _pageBindings = new Map<string, PageBinding>(); readonly _pageBindings = new Map<string, PageBinding>();
readonly _options: types.BrowserContextOptions; readonly _options: types.BrowserContextOptions;
_routes: { url: types.URLMatch, handler: network.RouteHandler }[] = []; _requestInterceptor?: network.RouteHandler;
private _isPersistentContext: boolean; private _isPersistentContext: boolean;
private _closedStatus: 'open' | 'closing' | 'closed' = 'open'; private _closedStatus: 'open' | 'closing' | 'closed' = 'open';
readonly _closePromise: Promise<Error>; readonly _closePromise: Promise<Error>;
@ -107,8 +106,7 @@ export abstract class BrowserContextBase extends EventEmitter implements Browser
abstract setOffline(offline: boolean): Promise<void>; abstract setOffline(offline: boolean): Promise<void>;
abstract _doAddInitScript(expression: string): Promise<void>; abstract _doAddInitScript(expression: string): Promise<void>;
abstract _doExposeBinding(binding: PageBinding): Promise<void>; abstract _doExposeBinding(binding: PageBinding): Promise<void>;
abstract route(url: types.URLMatch, handler: network.RouteHandler): Promise<void>; abstract _doUpdateRequestInterception(): Promise<void>;
abstract unroute(url: types.URLMatch, handler?: network.RouteHandler): Promise<void>;
abstract _doClose(): Promise<void>; abstract _doClose(): Promise<void>;
async cookies(urls: string | string[] | undefined = []): Promise<types.NetworkCookie[]> { async cookies(urls: string | string[] | undefined = []): Promise<types.NetworkCookie[]> {
@ -206,6 +204,11 @@ export abstract class BrowserContextBase extends EventEmitter implements Browser
this._options.httpCredentials = { username, password }; this._options.httpCredentials = { username, password };
} }
async _setRequestInterceptor(handler: network.RouteHandler | undefined): Promise<void> {
this._requestInterceptor = handler;
await this._doUpdateRequestInterception();
}
async close() { async close() {
if (this._isPersistentContext) { if (this._isPersistentContext) {
// Default context is only created in 'persistent' mode and closing it should close // Default context is only created in 'persistent' mode and closing it should close

View file

@ -416,14 +416,7 @@ export class CRBrowserContext extends BrowserContextBase {
await (page._delegate as CRPage).exposeBinding(binding); await (page._delegate as CRPage).exposeBinding(binding);
} }
async route(url: types.URLMatch, handler: network.RouteHandler): Promise<void> { async _doUpdateRequestInterception(): Promise<void> {
this._routes.push({ url, handler });
for (const page of this.pages())
await (page._delegate as CRPage).updateRequestInterception();
}
async unroute(url: types.URLMatch, handler?: network.RouteHandler): Promise<void> {
this._routes = this._routes.filter(route => route.url !== url || (handler && route.handler !== handler));
for (const page of this.pages()) for (const page of this.pages())
await (page._delegate as CRPage).updateRequestInterception(); await (page._delegate as CRPage).updateRequestInterception();
} }

View file

@ -191,7 +191,7 @@ export class CRNetworkManager {
if (requestPausedEvent) { if (requestPausedEvent) {
// CORS options request is generated by the network stack, it is not associated with the frame id. // CORS options request is generated by the network stack, it is not associated with the frame id.
// If URL matches interception pattern, accept it, assuming that this was intended when setting route. // If URL matches interception pattern, accept it, assuming that this was intended when setting route.
if (requestPausedEvent.request.method === 'OPTIONS' && this._page._isRouted(requestPausedEvent.request.url)) { if (requestPausedEvent.request.method === 'OPTIONS' && this._page._needsRequestInterception()) {
const requestHeaders = requestPausedEvent.request.headers; const requestHeaders = requestPausedEvent.request.headers;
const responseHeaders: Protocol.Fetch.HeaderEntry[] = [ const responseHeaders: Protocol.Fetch.HeaderEntry[] = [
{ name: 'Access-Control-Allow-Origin', value: requestHeaders['Origin'] || '*' }, { name: 'Access-Control-Allow-Origin', value: requestHeaders['Origin'] || '*' },

View file

@ -319,16 +319,8 @@ export class FFBrowserContext extends BrowserContextBase {
await this._browser._connection.send('Browser.addBinding', { browserContextId: this._browserContextId || undefined, name: binding.name, script: binding.source }); await this._browser._connection.send('Browser.addBinding', { browserContextId: this._browserContextId || undefined, name: binding.name, script: binding.source });
} }
async route(url: types.URLMatch, handler: network.RouteHandler): Promise<void> { async _doUpdateRequestInterception(): Promise<void> {
this._routes.push({ url, handler }); await this._browser._connection.send('Browser.setRequestInterception', { browserContextId: this._browserContextId || undefined, enabled: !!this._requestInterceptor });
if (this._routes.length === 1)
await this._browser._connection.send('Browser.setRequestInterception', { browserContextId: this._browserContextId || undefined, enabled: true });
}
async unroute(url: types.URLMatch, handler?: network.RouteHandler): Promise<void> {
this._routes = this._routes.filter(route => route.url !== url || (handler && route.handler !== handler));
if (this._routes.length === 0)
await this._browser._connection.send('Browser.setRequestInterception', { browserContextId: this._browserContextId || undefined, enabled: false });
} }
async _doClose() { async _doClose() {

View file

@ -113,7 +113,7 @@ export class Page extends EventEmitter {
private _workers = new Map<string, Worker>(); private _workers = new Map<string, Worker>();
readonly pdf: ((options?: types.PDFOptions) => Promise<Buffer>) | undefined; readonly pdf: ((options?: types.PDFOptions) => Promise<Buffer>) | undefined;
readonly coverage: any; readonly coverage: any;
_routes: { url: types.URLMatch, handler: network.RouteHandler }[] = []; private _requestInterceptor?: network.RouteHandler;
_ownedContext: BrowserContext | undefined; _ownedContext: BrowserContext | undefined;
constructor(delegate: PageDelegate, browserContext: BrowserContextBase) { constructor(delegate: PageDelegate, browserContext: BrowserContextBase) {
@ -291,16 +291,11 @@ export class Page extends EventEmitter {
} }
_needsRequestInterception(): boolean { _needsRequestInterception(): boolean {
return this._routes.length > 0 || this._browserContext._routes.length > 0; return !!this._requestInterceptor || !!this._browserContext._requestInterceptor;
} }
async route(url: types.URLMatch, handler: network.RouteHandler): Promise<void> { async _setRequestInterceptor(handler: network.RouteHandler | undefined): Promise<void> {
this._routes.push({ url, handler }); this._requestInterceptor = handler;
await this._delegate.updateRequestInterception();
}
async unroute(url: types.URLMatch, handler?: network.RouteHandler): Promise<void> {
this._routes = this._routes.filter(route => route.url !== url || (handler && route.handler !== handler));
await this._delegate.updateRequestInterception(); await this._delegate.updateRequestInterception();
} }
@ -309,33 +304,17 @@ export class Page extends EventEmitter {
const route = request._route(); const route = request._route();
if (!route) if (!route)
return; return;
for (const { url, handler } of this._routes) { if (this._requestInterceptor) {
if (helper.urlMatches(request.url(), url)) { this._requestInterceptor(route, request);
handler(route, request); return;
return;
}
} }
for (const { url, handler } of this._browserContext._routes) { if (this._browserContext._requestInterceptor) {
if (helper.urlMatches(request.url(), url)) { this._browserContext._requestInterceptor(route, request);
handler(route, request); return;
return;
}
} }
route.continue(); route.continue();
} }
_isRouted(requestURL: string): boolean {
for (const { url } of this._routes) {
if (helper.urlMatches(requestURL, url))
return true;
}
for (const { url } of this._browserContext._routes) {
if (helper.urlMatches(requestURL, url))
return true;
}
return false;
}
async screenshot(options: types.ScreenshotOptions = {}): Promise<Buffer> { async screenshot(options: types.ScreenshotOptions = {}): Promise<Buffer> {
return this._runAbortableTask( return this._runAbortableTask(
progress => this._screenshotter.screenshotPage(progress, options), progress => this._screenshotter.screenshotPage(progress, options),

View file

@ -112,10 +112,10 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, Browser
async setNetworkInterceptionEnabled(params: { enabled: boolean }): Promise<void> { async setNetworkInterceptionEnabled(params: { enabled: boolean }): Promise<void> {
if (!params.enabled) { if (!params.enabled) {
await this._context.unroute('**/*'); await this._context._setRequestInterceptor(undefined);
return; return;
} }
this._context.route('**/*', (route, request) => { this._context._setRequestInterceptor((route, request) => {
this._dispatchEvent('route', { route: new RouteDispatcher(this._scope, route), request: RequestDispatcher.from(this._scope, request) }); this._dispatchEvent('route', { route: new RouteDispatcher(this._scope, route), request: RequestDispatcher.from(this._scope, request) });
}); });
} }

View file

@ -123,10 +123,10 @@ export class PageDispatcher extends Dispatcher<Page, PageInitializer> implements
async setNetworkInterceptionEnabled(params: { enabled: boolean }): Promise<void> { async setNetworkInterceptionEnabled(params: { enabled: boolean }): Promise<void> {
if (!params.enabled) { if (!params.enabled) {
await this._page.unroute('**/*'); await this._page._setRequestInterceptor(undefined);
return; return;
} }
this._page.route('**/*', (route, request) => { this._page._setRequestInterceptor((route, request) => {
this._dispatchEvent('route', { route: new RouteDispatcher(this._scope, route), request: RequestDispatcher.from(this._scope, request) }); this._dispatchEvent('route', { route: new RouteDispatcher(this._scope, route), request: RequestDispatcher.from(this._scope, request) });
}); });
} }

View file

@ -322,14 +322,7 @@ export class WKBrowserContext extends BrowserContextBase {
await (page._delegate as WKPage).exposeBinding(binding); await (page._delegate as WKPage).exposeBinding(binding);
} }
async route(url: types.URLMatch, handler: network.RouteHandler): Promise<void> { async _doUpdateRequestInterception(): Promise<void> {
this._routes.push({ url, handler });
for (const page of this.pages())
await (page._delegate as WKPage).updateRequestInterception();
}
async unroute(url: types.URLMatch, handler?: network.RouteHandler): Promise<void> {
this._routes = this._routes.filter(route => route.url !== url || (handler && route.handler !== handler));
for (const page of this.pages()) for (const page of this.pages())
await (page._delegate as WKPage).updateRequestInterception(); await (page._delegate as WKPage).updateRequestInterception();
} }