chore: delete legacy response interception code (#9995)
This commit is contained in:
parent
cd47bf26e8
commit
5abb5d74ea
|
|
@ -215,99 +215,7 @@ export class Request extends ChannelOwner<channels.RequestChannel, channels.Requ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class InterceptedResponse implements api.Response {
|
|
||||||
private readonly _route: Route;
|
|
||||||
private readonly _initializer: channels.InterceptedResponse;
|
|
||||||
private readonly _request: Request;
|
|
||||||
private readonly _headers: RawHeaders;
|
|
||||||
|
|
||||||
constructor(route: Route, initializer: channels.InterceptedResponse) {
|
|
||||||
this._route = route;
|
|
||||||
this._initializer = initializer;
|
|
||||||
this._headers = new RawHeaders(initializer.headers);
|
|
||||||
this._request = Request.from(initializer.request);
|
|
||||||
}
|
|
||||||
|
|
||||||
async securityDetails(): Promise<{ issuer?: string | undefined; protocol?: string | undefined; subjectName?: string | undefined; validFrom?: number | undefined; validTo?: number | undefined; } | null> {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
async serverAddr(): Promise<{ ipAddress: string; port: number; } | null> {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
async finished(): Promise<Error | null> {
|
|
||||||
const response = await this._request.response();
|
|
||||||
if (!response)
|
|
||||||
return null;
|
|
||||||
return await response.finished();
|
|
||||||
}
|
|
||||||
|
|
||||||
frame(): api.Frame {
|
|
||||||
return this._request.frame();
|
|
||||||
}
|
|
||||||
|
|
||||||
ok(): boolean {
|
|
||||||
return this._initializer.status === 0 || (this._initializer.status >= 200 && this._initializer.status <= 299);
|
|
||||||
}
|
|
||||||
|
|
||||||
url(): string {
|
|
||||||
return this._request.url();
|
|
||||||
}
|
|
||||||
|
|
||||||
status(): number {
|
|
||||||
return this._initializer.status;
|
|
||||||
}
|
|
||||||
|
|
||||||
statusText(): string {
|
|
||||||
return this._initializer.statusText;
|
|
||||||
}
|
|
||||||
|
|
||||||
headers(): Headers {
|
|
||||||
return this._headers.headers();
|
|
||||||
}
|
|
||||||
|
|
||||||
async allHeaders(): Promise<Headers> {
|
|
||||||
return this.headers();
|
|
||||||
}
|
|
||||||
|
|
||||||
async headersArray(): Promise<HeadersArray> {
|
|
||||||
return this._headers.headersArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
async headerValue(name: string): Promise<string | null> {
|
|
||||||
return this._headers.get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
async headerValues(name: string): Promise<string[]> {
|
|
||||||
return this._headers.getAll(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
async body(): Promise<Buffer> {
|
|
||||||
return this._route._responseBody();
|
|
||||||
}
|
|
||||||
|
|
||||||
async text(): Promise<string> {
|
|
||||||
const content = await this.body();
|
|
||||||
return content.toString('utf8');
|
|
||||||
}
|
|
||||||
|
|
||||||
async json(): Promise<object> {
|
|
||||||
const content = await this.text();
|
|
||||||
return JSON.parse(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
request(): Request {
|
|
||||||
return this._request;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type InterceptResponse = true;
|
|
||||||
type NotInterceptResponse = false;
|
|
||||||
|
|
||||||
export class Route extends ChannelOwner<channels.RouteChannel, channels.RouteInitializer> implements api.Route {
|
export class Route extends ChannelOwner<channels.RouteChannel, channels.RouteInitializer> implements api.Route {
|
||||||
private _interceptedResponse: api.Response | undefined;
|
|
||||||
|
|
||||||
static from(route: channels.RouteChannel): Route {
|
static from(route: channels.RouteChannel): Route {
|
||||||
return (route as any)._object;
|
return (route as any)._object;
|
||||||
}
|
}
|
||||||
|
|
@ -326,25 +234,17 @@ export class Route extends ChannelOwner<channels.RouteChannel, channels.RouteIni
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async fulfill(options: { response?: api.Response | api.APIResponse, status?: number, headers?: Headers, contentType?: string, body?: string | Buffer, path?: string } = {}) {
|
async fulfill(options: { response?: api.APIResponse, status?: number, headers?: Headers, contentType?: string, body?: string | Buffer, path?: string } = {}) {
|
||||||
return this._wrapApiCall(async (channel: channels.RouteChannel) => {
|
return this._wrapApiCall(async (channel: channels.RouteChannel) => {
|
||||||
let useInterceptedResponseBody;
|
|
||||||
let fetchResponseUid;
|
let fetchResponseUid;
|
||||||
let { status: statusOption, headers: headersOption, body: bodyOption } = options;
|
let { status: statusOption, headers: headersOption, body } = options;
|
||||||
if (options.response) {
|
if (options.response) {
|
||||||
statusOption ||= options.response.status();
|
statusOption ||= options.response.status();
|
||||||
headersOption ||= options.response.headers();
|
headersOption ||= options.response.headers();
|
||||||
if (options.body === undefined && options.path === undefined) {
|
if (options.body === undefined && options.path === undefined && options.response instanceof FetchResponse)
|
||||||
if (options.response instanceof FetchResponse)
|
fetchResponseUid = (options.response as FetchResponse)._fetchUid();
|
||||||
fetchResponseUid = (options.response as FetchResponse)._fetchUid();
|
|
||||||
else if (options.response === this._interceptedResponse)
|
|
||||||
useInterceptedResponseBody = true;
|
|
||||||
else
|
|
||||||
bodyOption = await options.response.body();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let body = undefined;
|
|
||||||
let isBase64 = false;
|
let isBase64 = false;
|
||||||
let length = 0;
|
let length = 0;
|
||||||
if (options.path) {
|
if (options.path) {
|
||||||
|
|
@ -352,14 +252,13 @@ export class Route extends ChannelOwner<channels.RouteChannel, channels.RouteIni
|
||||||
body = buffer.toString('base64');
|
body = buffer.toString('base64');
|
||||||
isBase64 = true;
|
isBase64 = true;
|
||||||
length = buffer.length;
|
length = buffer.length;
|
||||||
} else if (isString(bodyOption)) {
|
} else if (isString(body)) {
|
||||||
body = bodyOption;
|
|
||||||
isBase64 = false;
|
isBase64 = false;
|
||||||
length = Buffer.byteLength(body);
|
length = Buffer.byteLength(body);
|
||||||
} else if (bodyOption) {
|
} else if (body) {
|
||||||
body = bodyOption.toString('base64');
|
length = body.length;
|
||||||
|
body = body.toString('base64');
|
||||||
isBase64 = true;
|
isBase64 = true;
|
||||||
length = bodyOption.length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const headers: Headers = {};
|
const headers: Headers = {};
|
||||||
|
|
@ -377,48 +276,30 @@ export class Route extends ChannelOwner<channels.RouteChannel, channels.RouteIni
|
||||||
headers: headersObjectToArray(headers),
|
headers: headersObjectToArray(headers),
|
||||||
body,
|
body,
|
||||||
isBase64,
|
isBase64,
|
||||||
useInterceptedResponseBody,
|
|
||||||
fetchResponseUid
|
fetchResponseUid
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async _continueToResponse(options: { url?: string, method?: string, headers?: Headers, postData?: string | Buffer, interceptResponse?: boolean } = {}): Promise<api.Response> {
|
|
||||||
this._interceptedResponse = await this._continue(options, true);
|
|
||||||
return this._interceptedResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
async continue(options: { url?: string, method?: string, headers?: Headers, postData?: string | Buffer } = {}) {
|
async continue(options: { url?: string, method?: string, headers?: Headers, postData?: string | Buffer } = {}) {
|
||||||
await this._continue(options, false);
|
await this._continue(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _internalContinue(options: { url?: string, method?: string, headers?: Headers, postData?: string | Buffer } = {}) {
|
async _internalContinue(options: { url?: string, method?: string, headers?: Headers, postData?: string | Buffer } = {}) {
|
||||||
await this._continue(options, false, true).catch(() => {});
|
await this._continue(options, true).catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
async _continue(options: { url?: string, method?: string, headers?: Headers, postData?: string | Buffer }, interceptResponse: NotInterceptResponse, isInternal?: boolean): Promise<null>;
|
private async _continue(options: { url?: string, method?: string, headers?: Headers, postData?: string | Buffer }, isInternal?: boolean) {
|
||||||
async _continue(options: { url?: string, method?: string, headers?: Headers, postData?: string | Buffer }, interceptResponse: InterceptResponse, isInternal?: boolean): Promise<api.Response>;
|
|
||||||
async _continue(options: { url?: string, method?: string, headers?: Headers, postData?: string | Buffer }, interceptResponse: boolean, isInternal?: boolean): Promise<null|api.Response> {
|
|
||||||
return await this._wrapApiCall(async (channel: channels.RouteChannel) => {
|
return await this._wrapApiCall(async (channel: channels.RouteChannel) => {
|
||||||
const postDataBuffer = isString(options.postData) ? Buffer.from(options.postData, 'utf8') : options.postData;
|
const postDataBuffer = isString(options.postData) ? Buffer.from(options.postData, 'utf8') : options.postData;
|
||||||
const result = await channel.continue({
|
await channel.continue({
|
||||||
url: options.url,
|
url: options.url,
|
||||||
method: options.method,
|
method: options.method,
|
||||||
headers: options.headers ? headersObjectToArray(options.headers) : undefined,
|
headers: options.headers ? headersObjectToArray(options.headers) : undefined,
|
||||||
postData: postDataBuffer ? postDataBuffer.toString('base64') : undefined,
|
postData: postDataBuffer ? postDataBuffer.toString('base64') : undefined,
|
||||||
interceptResponse,
|
|
||||||
});
|
});
|
||||||
if (result.response)
|
|
||||||
return new InterceptedResponse(this, result.response);
|
|
||||||
return null;
|
|
||||||
}, undefined, isInternal);
|
}, undefined, isInternal);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _responseBody(): Promise<Buffer> {
|
|
||||||
return this._wrapApiCall(async (channel: channels.RouteChannel) => {
|
|
||||||
return Buffer.from((await channel.responseBody()).binary, 'base64');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RouteHandlerCallback = (route: Route, request: Request) => void;
|
export type RouteHandlerCallback = (route: Route, request: Request) => void;
|
||||||
|
|
|
||||||
|
|
@ -113,28 +113,13 @@ export class RouteDispatcher extends Dispatcher<Route, channels.RouteInitializer
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async responseBody(params?: channels.RouteResponseBodyParams): Promise<channels.RouteResponseBodyResult> {
|
|
||||||
return { binary: (await this._object.responseBody()).toString('base64') };
|
|
||||||
}
|
|
||||||
|
|
||||||
async continue(params: channels.RouteContinueParams, metadata: CallMetadata): Promise<channels.RouteContinueResult> {
|
async continue(params: channels.RouteContinueParams, metadata: CallMetadata): Promise<channels.RouteContinueResult> {
|
||||||
const response = await this._object.continue({
|
await this._object.continue({
|
||||||
url: params.url,
|
url: params.url,
|
||||||
method: params.method,
|
method: params.method,
|
||||||
headers: params.headers,
|
headers: params.headers,
|
||||||
postData: params.postData ? Buffer.from(params.postData, 'base64') : undefined,
|
postData: params.postData ? Buffer.from(params.postData, 'base64') : undefined,
|
||||||
interceptResponse: params.interceptResponse
|
|
||||||
});
|
});
|
||||||
const result: channels.RouteContinueResult = {};
|
|
||||||
if (response) {
|
|
||||||
result.response = {
|
|
||||||
request: RequestDispatcher.from(this._scope, response.request()),
|
|
||||||
status: response.status(),
|
|
||||||
statusText: response.statusText(),
|
|
||||||
headers: response.headers(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fulfill(params: channels.RouteFulfillParams): Promise<void> {
|
async fulfill(params: channels.RouteFulfillParams): Promise<void> {
|
||||||
|
|
|
||||||
|
|
@ -161,13 +161,6 @@ export type FormField = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InterceptedResponse = {
|
|
||||||
request: RequestChannel,
|
|
||||||
status: number,
|
|
||||||
statusText: string,
|
|
||||||
headers: NameValue[],
|
|
||||||
};
|
|
||||||
|
|
||||||
// ----------- FetchRequest -----------
|
// ----------- FetchRequest -----------
|
||||||
export type FetchRequestInitializer = {};
|
export type FetchRequestInitializer = {};
|
||||||
export interface FetchRequestChannel extends Channel {
|
export interface FetchRequestChannel extends Channel {
|
||||||
|
|
@ -2797,7 +2790,6 @@ export interface RouteChannel extends Channel {
|
||||||
abort(params: RouteAbortParams, metadata?: Metadata): Promise<RouteAbortResult>;
|
abort(params: RouteAbortParams, metadata?: Metadata): Promise<RouteAbortResult>;
|
||||||
continue(params: RouteContinueParams, metadata?: Metadata): Promise<RouteContinueResult>;
|
continue(params: RouteContinueParams, metadata?: Metadata): Promise<RouteContinueResult>;
|
||||||
fulfill(params: RouteFulfillParams, metadata?: Metadata): Promise<RouteFulfillResult>;
|
fulfill(params: RouteFulfillParams, metadata?: Metadata): Promise<RouteFulfillResult>;
|
||||||
responseBody(params?: RouteResponseBodyParams, metadata?: Metadata): Promise<RouteResponseBodyResult>;
|
|
||||||
}
|
}
|
||||||
export type RouteAbortParams = {
|
export type RouteAbortParams = {
|
||||||
errorCode?: string,
|
errorCode?: string,
|
||||||
|
|
@ -2811,24 +2803,19 @@ export type RouteContinueParams = {
|
||||||
method?: string,
|
method?: string,
|
||||||
headers?: NameValue[],
|
headers?: NameValue[],
|
||||||
postData?: Binary,
|
postData?: Binary,
|
||||||
interceptResponse?: boolean,
|
|
||||||
};
|
};
|
||||||
export type RouteContinueOptions = {
|
export type RouteContinueOptions = {
|
||||||
url?: string,
|
url?: string,
|
||||||
method?: string,
|
method?: string,
|
||||||
headers?: NameValue[],
|
headers?: NameValue[],
|
||||||
postData?: Binary,
|
postData?: Binary,
|
||||||
interceptResponse?: boolean,
|
|
||||||
};
|
|
||||||
export type RouteContinueResult = {
|
|
||||||
response?: InterceptedResponse,
|
|
||||||
};
|
};
|
||||||
|
export type RouteContinueResult = void;
|
||||||
export type RouteFulfillParams = {
|
export type RouteFulfillParams = {
|
||||||
status?: number,
|
status?: number,
|
||||||
headers?: NameValue[],
|
headers?: NameValue[],
|
||||||
body?: string,
|
body?: string,
|
||||||
isBase64?: boolean,
|
isBase64?: boolean,
|
||||||
useInterceptedResponseBody?: boolean,
|
|
||||||
fetchResponseUid?: string,
|
fetchResponseUid?: string,
|
||||||
};
|
};
|
||||||
export type RouteFulfillOptions = {
|
export type RouteFulfillOptions = {
|
||||||
|
|
@ -2836,15 +2823,9 @@ export type RouteFulfillOptions = {
|
||||||
headers?: NameValue[],
|
headers?: NameValue[],
|
||||||
body?: string,
|
body?: string,
|
||||||
isBase64?: boolean,
|
isBase64?: boolean,
|
||||||
useInterceptedResponseBody?: boolean,
|
|
||||||
fetchResponseUid?: string,
|
fetchResponseUid?: string,
|
||||||
};
|
};
|
||||||
export type RouteFulfillResult = void;
|
export type RouteFulfillResult = void;
|
||||||
export type RouteResponseBodyParams = {};
|
|
||||||
export type RouteResponseBodyOptions = {};
|
|
||||||
export type RouteResponseBodyResult = {
|
|
||||||
binary: Binary,
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface RouteEvents {
|
export interface RouteEvents {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -227,17 +227,6 @@ FormField:
|
||||||
mimeType: string
|
mimeType: string
|
||||||
buffer: binary
|
buffer: binary
|
||||||
|
|
||||||
InterceptedResponse:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
request: Request
|
|
||||||
status: number
|
|
||||||
statusText: string
|
|
||||||
headers:
|
|
||||||
type: array
|
|
||||||
items: NameValue
|
|
||||||
|
|
||||||
|
|
||||||
FetchRequest:
|
FetchRequest:
|
||||||
type: interface
|
type: interface
|
||||||
|
|
||||||
|
|
@ -2327,9 +2316,6 @@ Route:
|
||||||
type: array?
|
type: array?
|
||||||
items: NameValue
|
items: NameValue
|
||||||
postData: binary?
|
postData: binary?
|
||||||
interceptResponse: boolean?
|
|
||||||
returns:
|
|
||||||
response: InterceptedResponse?
|
|
||||||
|
|
||||||
fulfill:
|
fulfill:
|
||||||
parameters:
|
parameters:
|
||||||
|
|
@ -2340,13 +2326,8 @@ Route:
|
||||||
items: NameValue
|
items: NameValue
|
||||||
body: string?
|
body: string?
|
||||||
isBase64: boolean?
|
isBase64: boolean?
|
||||||
useInterceptedResponseBody: boolean?
|
|
||||||
fetchResponseUid: string?
|
fetchResponseUid: string?
|
||||||
|
|
||||||
responseBody:
|
|
||||||
returns:
|
|
||||||
binary: binary
|
|
||||||
|
|
||||||
|
|
||||||
ResourceTiming:
|
ResourceTiming:
|
||||||
type: object
|
type: object
|
||||||
|
|
|
||||||
|
|
@ -157,12 +157,6 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||||
buffer: tBinary,
|
buffer: tBinary,
|
||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
scheme.InterceptedResponse = tObject({
|
|
||||||
request: tChannel('Request'),
|
|
||||||
status: tNumber,
|
|
||||||
statusText: tString,
|
|
||||||
headers: tArray(tType('NameValue')),
|
|
||||||
});
|
|
||||||
scheme.FetchRequestFetchParams = tObject({
|
scheme.FetchRequestFetchParams = tObject({
|
||||||
url: tString,
|
url: tString,
|
||||||
params: tOptional(tArray(tType('NameValue'))),
|
params: tOptional(tArray(tType('NameValue'))),
|
||||||
|
|
@ -1108,17 +1102,14 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||||
method: tOptional(tString),
|
method: tOptional(tString),
|
||||||
headers: tOptional(tArray(tType('NameValue'))),
|
headers: tOptional(tArray(tType('NameValue'))),
|
||||||
postData: tOptional(tBinary),
|
postData: tOptional(tBinary),
|
||||||
interceptResponse: tOptional(tBoolean),
|
|
||||||
});
|
});
|
||||||
scheme.RouteFulfillParams = tObject({
|
scheme.RouteFulfillParams = tObject({
|
||||||
status: tOptional(tNumber),
|
status: tOptional(tNumber),
|
||||||
headers: tOptional(tArray(tType('NameValue'))),
|
headers: tOptional(tArray(tType('NameValue'))),
|
||||||
body: tOptional(tString),
|
body: tOptional(tString),
|
||||||
isBase64: tOptional(tBoolean),
|
isBase64: tOptional(tBoolean),
|
||||||
useInterceptedResponseBody: tOptional(tBoolean),
|
|
||||||
fetchResponseUid: tOptional(tString),
|
fetchResponseUid: tOptional(tString),
|
||||||
});
|
});
|
||||||
scheme.RouteResponseBodyParams = tOptional(tObject({}));
|
|
||||||
scheme.ResourceTiming = tObject({
|
scheme.ResourceTiming = tObject({
|
||||||
startTime: tNumber,
|
startTime: tNumber,
|
||||||
domainLookupStart: tNumber,
|
domainLookupStart: tNumber,
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ export class CRNetworkManager {
|
||||||
this._client.send('Network.setCacheDisabled', { cacheDisabled: true }),
|
this._client.send('Network.setCacheDisabled', { cacheDisabled: true }),
|
||||||
this._client.send('Fetch.enable', {
|
this._client.send('Fetch.enable', {
|
||||||
handleAuthRequests: true,
|
handleAuthRequests: true,
|
||||||
patterns: [{ urlPattern: '*', requestStage: 'Request' }, { urlPattern: '*', requestStage: 'Response' }],
|
patterns: [{ urlPattern: '*', requestStage: 'Request' }],
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -179,20 +179,6 @@ export class CRNetworkManager {
|
||||||
if (event.request.url.startsWith('data:'))
|
if (event.request.url.startsWith('data:'))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (event.responseStatusCode || event.responseErrorReason) {
|
|
||||||
const isRedirect = event.responseStatusCode && event.responseStatusCode >= 300 && event.responseStatusCode < 400;
|
|
||||||
const request = this._requestIdToRequest.get(event.networkId!);
|
|
||||||
const route = request?._routeForRedirectChain();
|
|
||||||
if (isRedirect || !route || !route._interceptingResponse) {
|
|
||||||
this._client._sendMayFail('Fetch.continueRequest', {
|
|
||||||
requestId: event.requestId
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
route._responseInterceptedCallback(event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const requestId = event.networkId;
|
const requestId = event.networkId;
|
||||||
const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(requestId);
|
const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(requestId);
|
||||||
if (requestWillBeSentEvent) {
|
if (requestWillBeSentEvent) {
|
||||||
|
|
@ -476,24 +462,14 @@ class InterceptableRequest {
|
||||||
class RouteImpl implements network.RouteDelegate {
|
class RouteImpl implements network.RouteDelegate {
|
||||||
private readonly _client: CRSession;
|
private readonly _client: CRSession;
|
||||||
private _interceptionId: string;
|
private _interceptionId: string;
|
||||||
private _responseInterceptedPromise: Promise<Protocol.Fetch.requestPausedPayload>;
|
|
||||||
_responseInterceptedCallback: ((event: Protocol.Fetch.requestPausedPayload) => void) = () => {};
|
|
||||||
_interceptingResponse: boolean = false;
|
|
||||||
_wasFulfilled = false;
|
_wasFulfilled = false;
|
||||||
|
|
||||||
constructor(client: CRSession, interceptionId: string) {
|
constructor(client: CRSession, interceptionId: string) {
|
||||||
this._client = client;
|
this._client = client;
|
||||||
this._interceptionId = interceptionId;
|
this._interceptionId = interceptionId;
|
||||||
this._responseInterceptedPromise = new Promise(resolve => this._responseInterceptedCallback = resolve);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async responseBody(): Promise<Buffer> {
|
async continue(request: network.Request, overrides: types.NormalizedContinueOverrides): Promise<void> {
|
||||||
const response = await this._client.send('Fetch.getResponseBody', { requestId: this._interceptionId! });
|
|
||||||
return Buffer.from(response.body, response.base64Encoded ? 'base64' : 'utf8');
|
|
||||||
}
|
|
||||||
|
|
||||||
async continue(request: network.Request, overrides: types.NormalizedContinueOverrides): Promise<network.InterceptedResponse|null> {
|
|
||||||
this._interceptingResponse = !!overrides.interceptResponse;
|
|
||||||
// In certain cases, protocol will return error if the request was already canceled
|
// In certain cases, protocol will return error if the request was already canceled
|
||||||
// or the page was closed. We should tolerate these errors.
|
// or the page was closed. We should tolerate these errors.
|
||||||
await this._client._sendMayFail('Fetch.continueRequest', {
|
await this._client._sendMayFail('Fetch.continueRequest', {
|
||||||
|
|
@ -503,18 +479,6 @@ class RouteImpl implements network.RouteDelegate {
|
||||||
method: overrides.method,
|
method: overrides.method,
|
||||||
postData: overrides.postData ? overrides.postData.toString('base64') : undefined
|
postData: overrides.postData ? overrides.postData.toString('base64') : undefined
|
||||||
});
|
});
|
||||||
if (!this._interceptingResponse)
|
|
||||||
return null;
|
|
||||||
const event = await this._responseInterceptedPromise;
|
|
||||||
this._interceptionId = event.requestId;
|
|
||||||
// FIXME: plumb status text from browser
|
|
||||||
if (event.responseErrorReason) {
|
|
||||||
this._client._sendMayFail('Fetch.continueRequest', {
|
|
||||||
requestId: event.requestId
|
|
||||||
});
|
|
||||||
throw new Error(`Request failed: ${event.responseErrorReason}`);
|
|
||||||
}
|
|
||||||
return new network.InterceptedResponse(request, event.responseStatusCode!, '', event.responseHeaders!);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fulfill(response: types.NormalizedFulfillResponse) {
|
async fulfill(response: types.NormalizedFulfillResponse) {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import * as network from '../network';
|
||||||
import * as frames from '../frames';
|
import * as frames from '../frames';
|
||||||
import * as types from '../types';
|
import * as types from '../types';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
import { InterceptedResponse } from '../network';
|
|
||||||
import { HeadersArray } from '../../server/types';
|
import { HeadersArray } from '../../server/types';
|
||||||
|
|
||||||
export class FFNetworkManager {
|
export class FFNetworkManager {
|
||||||
|
|
@ -212,27 +211,14 @@ class FFRouteImpl implements network.RouteDelegate {
|
||||||
this._request = request;
|
this._request = request;
|
||||||
}
|
}
|
||||||
|
|
||||||
async responseBody(): Promise<Buffer> {
|
async continue(request: network.Request, overrides: types.NormalizedContinueOverrides) {
|
||||||
const response = await this._session.send('Network.getResponseBody', {
|
await this._session.sendMayFail('Network.resumeInterceptedRequest', {
|
||||||
requestId: this._request._finalRequest()._id
|
|
||||||
});
|
|
||||||
return Buffer.from(response.base64body, 'base64');
|
|
||||||
}
|
|
||||||
|
|
||||||
async continue(request: network.Request, overrides: types.NormalizedContinueOverrides): Promise<network.InterceptedResponse|null> {
|
|
||||||
const result = await this._session.sendMayFail('Network.resumeInterceptedRequest', {
|
|
||||||
requestId: this._request._id,
|
requestId: this._request._id,
|
||||||
url: overrides.url,
|
url: overrides.url,
|
||||||
method: overrides.method,
|
method: overrides.method,
|
||||||
headers: overrides.headers,
|
headers: overrides.headers,
|
||||||
postData: overrides.postData ? Buffer.from(overrides.postData).toString('base64') : undefined,
|
postData: overrides.postData ? Buffer.from(overrides.postData).toString('base64') : undefined,
|
||||||
interceptResponse: overrides.interceptResponse,
|
});
|
||||||
}) as any;
|
|
||||||
if (!overrides.interceptResponse)
|
|
||||||
return null;
|
|
||||||
if (result.error)
|
|
||||||
throw new Error(`Request failed: ${result.error}`);
|
|
||||||
return new InterceptedResponse(request, result.response.status, result.response.statusText, result.response.headers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fulfill(response: types.NormalizedFulfillResponse) {
|
async fulfill(response: types.NormalizedFulfillResponse) {
|
||||||
|
|
|
||||||
|
|
@ -226,7 +226,6 @@ export class Route extends SdkObject {
|
||||||
private readonly _request: Request;
|
private readonly _request: Request;
|
||||||
private readonly _delegate: RouteDelegate;
|
private readonly _delegate: RouteDelegate;
|
||||||
private _handled = false;
|
private _handled = false;
|
||||||
private _response: InterceptedResponse | null = null;
|
|
||||||
|
|
||||||
constructor(request: Request, delegate: RouteDelegate) {
|
constructor(request: Request, delegate: RouteDelegate) {
|
||||||
super(request.frame(), 'route');
|
super(request.frame(), 'route');
|
||||||
|
|
@ -256,9 +255,6 @@ export class Route extends SdkObject {
|
||||||
assert(buffer, 'Fetch response has been disposed');
|
assert(buffer, 'Fetch response has been disposed');
|
||||||
body = buffer.toString('base64');
|
body = buffer.toString('base64');
|
||||||
isBase64 = true;
|
isBase64 = true;
|
||||||
} else if (this._response && overrides.useInterceptedResponseBody) {
|
|
||||||
body = (await this._delegate.responseBody()).toString('base64');
|
|
||||||
isBase64 = true;
|
|
||||||
} else {
|
} else {
|
||||||
body = '';
|
body = '';
|
||||||
isBase64 = false;
|
isBase64 = false;
|
||||||
|
|
@ -272,22 +268,15 @@ export class Route extends SdkObject {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async continue(overrides: types.NormalizedContinueOverrides = {}): Promise<InterceptedResponse|null> {
|
async continue(overrides: types.NormalizedContinueOverrides = {}) {
|
||||||
assert(!this._handled, 'Route is already handled!');
|
assert(!this._handled, 'Route is already handled!');
|
||||||
assert(!this._response, 'Cannot call continue after response interception!');
|
|
||||||
if (overrides.url) {
|
if (overrides.url) {
|
||||||
const newUrl = new URL(overrides.url);
|
const newUrl = new URL(overrides.url);
|
||||||
const oldUrl = new URL(this._request.url());
|
const oldUrl = new URL(this._request.url());
|
||||||
if (oldUrl.protocol !== newUrl.protocol)
|
if (oldUrl.protocol !== newUrl.protocol)
|
||||||
throw new Error('New URL must have same protocol as overridden URL');
|
throw new Error('New URL must have same protocol as overridden URL');
|
||||||
}
|
}
|
||||||
this._response = await this._delegate.continue(this._request, overrides);
|
await this._delegate.continue(this._request, overrides);
|
||||||
return this._response;
|
|
||||||
}
|
|
||||||
|
|
||||||
async responseBody(): Promise<Buffer> {
|
|
||||||
assert(!this._handled, 'Route is already handled!');
|
|
||||||
return this._delegate.responseBody();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -485,37 +474,6 @@ export class Response extends SdkObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class InterceptedResponse extends SdkObject {
|
|
||||||
private readonly _request: Request;
|
|
||||||
private readonly _status: number;
|
|
||||||
private readonly _statusText: string;
|
|
||||||
private readonly _headers: types.HeadersArray;
|
|
||||||
|
|
||||||
constructor(request: Request, status: number, statusText: string, headers: types.HeadersArray) {
|
|
||||||
super(request.frame(), 'interceptedResponse');
|
|
||||||
this._request = request._finalRequest();
|
|
||||||
this._status = status;
|
|
||||||
this._statusText = statusText;
|
|
||||||
this._headers = headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
status(): number {
|
|
||||||
return this._status;
|
|
||||||
}
|
|
||||||
|
|
||||||
statusText(): string {
|
|
||||||
return this._statusText;
|
|
||||||
}
|
|
||||||
|
|
||||||
headers(): types.HeadersArray {
|
|
||||||
return this._headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
request(): Request {
|
|
||||||
return this._request;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class WebSocket extends SdkObject {
|
export class WebSocket extends SdkObject {
|
||||||
private _url: string;
|
private _url: string;
|
||||||
|
|
||||||
|
|
@ -555,8 +513,7 @@ export class WebSocket extends SdkObject {
|
||||||
export interface RouteDelegate {
|
export interface RouteDelegate {
|
||||||
abort(errorCode: string): Promise<void>;
|
abort(errorCode: string): Promise<void>;
|
||||||
fulfill(response: types.NormalizedFulfillResponse): Promise<void>;
|
fulfill(response: types.NormalizedFulfillResponse): Promise<void>;
|
||||||
continue(request: Request, overrides: types.NormalizedContinueOverrides): Promise<InterceptedResponse|null>;
|
continue(request: Request, overrides: types.NormalizedContinueOverrides): Promise<void>;
|
||||||
responseBody(): Promise<Buffer>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// List taken from https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml with extra 306 and 418 codes.
|
// List taken from https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml with extra 306 and 418 codes.
|
||||||
|
|
|
||||||
|
|
@ -211,7 +211,6 @@ export type NormalizedContinueOverrides = {
|
||||||
method?: string,
|
method?: string,
|
||||||
headers?: HeadersArray,
|
headers?: HeadersArray,
|
||||||
postData?: Buffer,
|
postData?: Buffer,
|
||||||
interceptResponse?: boolean,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type NetworkCookie = {
|
export type NetworkCookie = {
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,6 @@ import * as types from '../types';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
import { WKSession } from './wkConnection';
|
import { WKSession } from './wkConnection';
|
||||||
import { assert, headersObjectToArray, headersArrayToObject } from '../../utils/utils';
|
import { assert, headersObjectToArray, headersArrayToObject } from '../../utils/utils';
|
||||||
import { InterceptedResponse } from '../network';
|
|
||||||
import { WKPage } from './wkPage';
|
|
||||||
import { ManualPromise } from '../../utils/async';
|
import { ManualPromise } from '../../utils/async';
|
||||||
|
|
||||||
const errorReasons: { [reason: string]: Protocol.Network.ResourceErrorType } = {
|
const errorReasons: { [reason: string]: Protocol.Network.ResourceErrorType } = {
|
||||||
|
|
@ -98,28 +96,19 @@ export class WKRouteImpl implements network.RouteDelegate {
|
||||||
private readonly _session: WKSession;
|
private readonly _session: WKSession;
|
||||||
private readonly _requestId: string;
|
private readonly _requestId: string;
|
||||||
readonly _requestInterceptedPromise = new ManualPromise<void>();
|
readonly _requestInterceptedPromise = new ManualPromise<void>();
|
||||||
_responseInterceptedPromise: ManualPromise<{ response?: Protocol.Network.Response, error?: Protocol.Network.loadingFailedPayload }> | undefined;
|
|
||||||
private readonly _page: WKPage;
|
|
||||||
|
|
||||||
constructor(session: WKSession, page: WKPage, requestId: string) {
|
constructor(session: WKSession, requestId: string) {
|
||||||
this._session = session;
|
this._session = session;
|
||||||
this._page = page;
|
|
||||||
this._requestId = requestId;
|
this._requestId = requestId;
|
||||||
}
|
}
|
||||||
|
|
||||||
async responseBody(): Promise<Buffer> {
|
|
||||||
const response = await this._session.send('Network.getInterceptedResponseBody', { requestId: this._requestId });
|
|
||||||
return Buffer.from(response.body, 'base64');
|
|
||||||
}
|
|
||||||
|
|
||||||
async abort(errorCode: string) {
|
async abort(errorCode: string) {
|
||||||
const errorType = errorReasons[errorCode];
|
const errorType = errorReasons[errorCode];
|
||||||
assert(errorType, 'Unknown error code: ' + errorCode);
|
assert(errorType, 'Unknown error code: ' + errorCode);
|
||||||
await this._requestInterceptedPromise;
|
await this._requestInterceptedPromise;
|
||||||
const isResponseIntercepted = await this._responseInterceptedPromise;
|
|
||||||
// In certain cases, protocol will return error if the request was already canceled
|
// In certain cases, protocol will return error if the request was already canceled
|
||||||
// or the page was closed. We should tolerate these errors.
|
// or the page was closed. We should tolerate these errors.
|
||||||
await this._session.sendMayFail(isResponseIntercepted ? 'Network.interceptResponseWithError' : 'Network.interceptRequestWithError', { requestId: this._requestId, errorType });
|
await this._session.sendMayFail('Network.interceptRequestWithError', { requestId: this._requestId, errorType });
|
||||||
}
|
}
|
||||||
|
|
||||||
async fulfill(response: types.NormalizedFulfillResponse) {
|
async fulfill(response: types.NormalizedFulfillResponse) {
|
||||||
|
|
@ -135,8 +124,7 @@ export class WKRouteImpl implements network.RouteDelegate {
|
||||||
if (contentType)
|
if (contentType)
|
||||||
mimeType = contentType.split(';')[0].trim();
|
mimeType = contentType.split(';')[0].trim();
|
||||||
|
|
||||||
const isResponseIntercepted = await this._responseInterceptedPromise;
|
await this._session.sendMayFail('Network.interceptRequestWithResponse', {
|
||||||
await this._session.sendMayFail(isResponseIntercepted ? 'Network.interceptWithResponse' : 'Network.interceptRequestWithResponse', {
|
|
||||||
requestId: this._requestId,
|
requestId: this._requestId,
|
||||||
status: response.status,
|
status: response.status,
|
||||||
statusText: network.STATUS_TEXTS[String(response.status)],
|
statusText: network.STATUS_TEXTS[String(response.status)],
|
||||||
|
|
@ -147,11 +135,7 @@ export class WKRouteImpl implements network.RouteDelegate {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async continue(request: network.Request, overrides: types.NormalizedContinueOverrides): Promise<network.InterceptedResponse|null> {
|
async continue(request: network.Request, overrides: types.NormalizedContinueOverrides) {
|
||||||
if (overrides.interceptResponse) {
|
|
||||||
await this._page._ensureResponseInterceptionEnabled();
|
|
||||||
this._responseInterceptedPromise = new ManualPromise();
|
|
||||||
}
|
|
||||||
await this._requestInterceptedPromise;
|
await this._requestInterceptedPromise;
|
||||||
// In certain cases, protocol will return error if the request was already canceled
|
// In certain cases, protocol will return error if the request was already canceled
|
||||||
// or the page was closed. We should tolerate these errors.
|
// or the page was closed. We should tolerate these errors.
|
||||||
|
|
@ -162,12 +146,6 @@ export class WKRouteImpl implements network.RouteDelegate {
|
||||||
headers: overrides.headers ? headersArrayToObject(overrides.headers, false /* lowerCase */) : undefined,
|
headers: overrides.headers ? headersArrayToObject(overrides.headers, false /* lowerCase */) : undefined,
|
||||||
postData: overrides.postData ? Buffer.from(overrides.postData).toString('base64') : undefined
|
postData: overrides.postData ? Buffer.from(overrides.postData).toString('base64') : undefined
|
||||||
});
|
});
|
||||||
if (!this._responseInterceptedPromise)
|
|
||||||
return null;
|
|
||||||
const { response, error } = await this._responseInterceptedPromise;
|
|
||||||
if (error)
|
|
||||||
throw new Error(`Request failed: ${error.errorText}`);
|
|
||||||
return new InterceptedResponse(request, response!.status, response!.statusText, headersObjectToArray(response!.headers));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,6 @@ export class WKPage implements PageDelegate {
|
||||||
private _lastConsoleMessage: { derivedType: string, text: string, handles: JSHandle[]; count: number, location: types.ConsoleMessageLocation; } | null = null;
|
private _lastConsoleMessage: { derivedType: string, text: string, handles: JSHandle[]; count: number, location: types.ConsoleMessageLocation; } | null = null;
|
||||||
|
|
||||||
private readonly _requestIdToResponseReceivedPayloadEvent = new Map<string, Protocol.Network.responseReceivedPayload>();
|
private readonly _requestIdToResponseReceivedPayloadEvent = new Map<string, Protocol.Network.responseReceivedPayload>();
|
||||||
_needsResponseInterception: boolean = false;
|
|
||||||
// Holds window features for the next popup being opened via window.open,
|
// Holds window features for the next popup being opened via window.open,
|
||||||
// until the popup page proxy arrives.
|
// until the popup page proxy arrives.
|
||||||
private _nextWindowOpenPopupFeatures?: string[];
|
private _nextWindowOpenPopupFeatures?: string[];
|
||||||
|
|
@ -178,8 +177,6 @@ export class WKPage implements PageDelegate {
|
||||||
if (this._page._needsRequestInterception()) {
|
if (this._page._needsRequestInterception()) {
|
||||||
promises.push(session.send('Network.setInterceptionEnabled', { enabled: true }));
|
promises.push(session.send('Network.setInterceptionEnabled', { enabled: true }));
|
||||||
promises.push(session.send('Network.addInterception', { url: '.*', stage: 'request', isRegex: true }));
|
promises.push(session.send('Network.addInterception', { url: '.*', stage: 'request', isRegex: true }));
|
||||||
if (this._needsResponseInterception)
|
|
||||||
promises.push(session.send('Network.addInterception', { url: '.*', stage: 'response', isRegex: true }));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const contextOptions = this._browserContext._options;
|
const contextOptions = this._browserContext._options;
|
||||||
|
|
@ -375,7 +372,6 @@ export class WKPage implements PageDelegate {
|
||||||
eventsHelper.addEventListener(this._session, 'Page.fileChooserOpened', event => this._onFileChooserOpened(event)),
|
eventsHelper.addEventListener(this._session, 'Page.fileChooserOpened', event => this._onFileChooserOpened(event)),
|
||||||
eventsHelper.addEventListener(this._session, 'Network.requestWillBeSent', e => this._onRequestWillBeSent(this._session, e)),
|
eventsHelper.addEventListener(this._session, 'Network.requestWillBeSent', e => this._onRequestWillBeSent(this._session, e)),
|
||||||
eventsHelper.addEventListener(this._session, 'Network.requestIntercepted', e => this._onRequestIntercepted(this._session, e)),
|
eventsHelper.addEventListener(this._session, 'Network.requestIntercepted', e => this._onRequestIntercepted(this._session, e)),
|
||||||
eventsHelper.addEventListener(this._session, 'Network.responseIntercepted', e => this._onResponseIntercepted(this._session, e)),
|
|
||||||
eventsHelper.addEventListener(this._session, 'Network.responseReceived', e => this._onResponseReceived(e)),
|
eventsHelper.addEventListener(this._session, 'Network.responseReceived', e => this._onResponseReceived(e)),
|
||||||
eventsHelper.addEventListener(this._session, 'Network.loadingFinished', e => this._onLoadingFinished(e)),
|
eventsHelper.addEventListener(this._session, 'Network.loadingFinished', e => this._onLoadingFinished(e)),
|
||||||
eventsHelper.addEventListener(this._session, 'Network.loadingFailed', e => this._onLoadingFailed(e)),
|
eventsHelper.addEventListener(this._session, 'Network.loadingFailed', e => this._onLoadingFailed(e)),
|
||||||
|
|
@ -671,22 +667,12 @@ export class WKPage implements PageDelegate {
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _ensureResponseInterceptionEnabled() {
|
|
||||||
if (this._needsResponseInterception)
|
|
||||||
return;
|
|
||||||
this._needsResponseInterception = true;
|
|
||||||
await this.updateRequestInterception();
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateRequestInterception(): Promise<void> {
|
async updateRequestInterception(): Promise<void> {
|
||||||
const enabled = this._page._needsRequestInterception();
|
const enabled = this._page._needsRequestInterception();
|
||||||
const promises = [
|
await Promise.all([
|
||||||
this._updateState('Network.setInterceptionEnabled', { enabled }),
|
this._updateState('Network.setInterceptionEnabled', { enabled }),
|
||||||
this._updateState('Network.addInterception', { url: '.*', stage: 'request', isRegex: true }),
|
this._updateState('Network.addInterception', { url: '.*', stage: 'request', isRegex: true }),
|
||||||
];
|
]);
|
||||||
if (this._needsResponseInterception)
|
|
||||||
this._updateState('Network.addInterception', { url: '.*', stage: 'response', isRegex: true });
|
|
||||||
await Promise.all(promises);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateOffline() {
|
async updateOffline() {
|
||||||
|
|
@ -970,7 +956,7 @@ export class WKPage implements PageDelegate {
|
||||||
let route = null;
|
let route = null;
|
||||||
// We do not support intercepting redirects.
|
// We do not support intercepting redirects.
|
||||||
if (this._page._needsRequestInterception() && !redirectedFrom)
|
if (this._page._needsRequestInterception() && !redirectedFrom)
|
||||||
route = new WKRouteImpl(session, this, event.requestId);
|
route = new WKRouteImpl(session, event.requestId);
|
||||||
const request = new WKInterceptableRequest(session, route, frame, event, redirectedFrom, documentId);
|
const request = new WKInterceptableRequest(session, route, frame, event, redirectedFrom, documentId);
|
||||||
this._requestIdToRequest.set(event.requestId, request);
|
this._requestIdToRequest.set(event.requestId, request);
|
||||||
this._page._frameManager.requestStarted(request.request, route || undefined);
|
this._page._frameManager.requestStarted(request.request, route || undefined);
|
||||||
|
|
@ -1001,16 +987,6 @@ export class WKPage implements PageDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onResponseIntercepted(session: WKSession, event: Protocol.Network.responseInterceptedPayload) {
|
|
||||||
const request = this._requestIdToRequest.get(event.requestId);
|
|
||||||
const route = request?._routeForRedirectChain();
|
|
||||||
if (!route?._responseInterceptedPromise) {
|
|
||||||
session.sendMayFail('Network.interceptContinue', { requestId: event.requestId, stage: 'response' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
route._responseInterceptedPromise.resolve({ response: event.response });
|
|
||||||
}
|
|
||||||
|
|
||||||
_onResponseReceived(event: Protocol.Network.responseReceivedPayload) {
|
_onResponseReceived(event: Protocol.Network.responseReceivedPayload) {
|
||||||
const request = this._requestIdToRequest.get(event.requestId);
|
const request = this._requestIdToRequest.get(event.requestId);
|
||||||
// FileUpload sends a response without a matching request.
|
// FileUpload sends a response without a matching request.
|
||||||
|
|
@ -1075,9 +1051,6 @@ export class WKPage implements PageDelegate {
|
||||||
// @see https://crbug.com/750469
|
// @see https://crbug.com/750469
|
||||||
if (!request)
|
if (!request)
|
||||||
return;
|
return;
|
||||||
const route = request._routeForRedirectChain();
|
|
||||||
if (route?._responseInterceptedPromise)
|
|
||||||
route._responseInterceptedPromise.resolve({ error: event });
|
|
||||||
const response = request.request._existingResponse();
|
const response = request.request._existingResponse();
|
||||||
if (response) {
|
if (response) {
|
||||||
response._serverAddrFinished();
|
response._serverAddrFinished();
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,6 @@ export class WKProvisionalPage {
|
||||||
this._sessionListeners = [
|
this._sessionListeners = [
|
||||||
eventsHelper.addEventListener(session, 'Network.requestWillBeSent', overrideFrameId(e => wkPage._onRequestWillBeSent(session, e))),
|
eventsHelper.addEventListener(session, 'Network.requestWillBeSent', overrideFrameId(e => wkPage._onRequestWillBeSent(session, e))),
|
||||||
eventsHelper.addEventListener(session, 'Network.requestIntercepted', overrideFrameId(e => wkPage._onRequestIntercepted(session, e))),
|
eventsHelper.addEventListener(session, 'Network.requestIntercepted', overrideFrameId(e => wkPage._onRequestIntercepted(session, e))),
|
||||||
eventsHelper.addEventListener(session, 'Network.responseIntercepted', overrideFrameId(e => wkPage._onResponseIntercepted(session, e))),
|
|
||||||
eventsHelper.addEventListener(session, 'Network.responseReceived', overrideFrameId(e => wkPage._onResponseReceived(e))),
|
eventsHelper.addEventListener(session, 'Network.responseReceived', overrideFrameId(e => wkPage._onResponseReceived(e))),
|
||||||
eventsHelper.addEventListener(session, 'Network.loadingFinished', overrideFrameId(e => wkPage._onLoadingFinished(e))),
|
eventsHelper.addEventListener(session, 'Network.loadingFinished', overrideFrameId(e => wkPage._onLoadingFinished(e))),
|
||||||
eventsHelper.addEventListener(session, 'Network.loadingFailed', overrideFrameId(e => wkPage._onLoadingFailed(e))),
|
eventsHelper.addEventListener(session, 'Network.loadingFailed', overrideFrameId(e => wkPage._onLoadingFailed(e))),
|
||||||
|
|
|
||||||
|
|
@ -15,16 +15,15 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { fail } from 'assert';
|
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import type { Route, Response } from 'playwright-core';
|
import type { Route } from 'playwright-core';
|
||||||
import { expect, test as it } from './pageTest';
|
import { expect, test as it } from './pageTest';
|
||||||
|
|
||||||
it('should fulfill intercepted response', async ({ page, server, browserName }) => {
|
it('should fulfill intercepted response', async ({ page, server }) => {
|
||||||
await page.route('**/*', async route => {
|
await page.route('**/*', async route => {
|
||||||
// @ts-expect-error
|
const response = await page.request.fetch(route.request());
|
||||||
await route._continueToResponse({});
|
|
||||||
await route.fulfill({
|
await route.fulfill({
|
||||||
|
response,
|
||||||
status: 201,
|
status: 201,
|
||||||
headers: {
|
headers: {
|
||||||
foo: 'bar'
|
foo: 'bar'
|
||||||
|
|
@ -43,8 +42,7 @@ it('should fulfill intercepted response', async ({ page, server, browserName })
|
||||||
it('should fulfill response with empty body', async ({ page, server, browserName, browserMajorVersion }) => {
|
it('should fulfill response with empty body', async ({ page, server, browserName, browserMajorVersion }) => {
|
||||||
it.skip(browserName === 'chromium' && browserMajorVersion <= 91, 'Fails in Electron that uses old Chromium');
|
it.skip(browserName === 'chromium' && browserMajorVersion <= 91, 'Fails in Electron that uses old Chromium');
|
||||||
await page.route('**/*', async route => {
|
await page.route('**/*', async route => {
|
||||||
// @ts-expect-error
|
const response = await page.request.fetch(route.request());
|
||||||
const response = await route._continueToResponse({});
|
|
||||||
await route.fulfill({
|
await route.fulfill({
|
||||||
response,
|
response,
|
||||||
status: 201,
|
status: 201,
|
||||||
|
|
@ -63,8 +61,7 @@ it('should override with defaults when intercepted response not provided', async
|
||||||
res.end('my content');
|
res.end('my content');
|
||||||
});
|
});
|
||||||
await page.route('**/*', async route => {
|
await page.route('**/*', async route => {
|
||||||
// @ts-expect-error
|
await page.request.fetch(route.request());
|
||||||
await route._continueToResponse({});
|
|
||||||
await route.fulfill({
|
await route.fulfill({
|
||||||
status: 201,
|
status: 201,
|
||||||
});
|
});
|
||||||
|
|
@ -85,14 +82,10 @@ it('should fulfill with any response', async ({ page, server, browserName, brows
|
||||||
res.setHeader('foo', 'bar');
|
res.setHeader('foo', 'bar');
|
||||||
res.end('Woo-hoo');
|
res.end('Woo-hoo');
|
||||||
});
|
});
|
||||||
const page2 = await page.context().newPage();
|
const sampleResponse = await page.request.get(`${server.PREFIX}/sample`);
|
||||||
const sampleResponse = await page2.goto(`${server.PREFIX}/sample`);
|
|
||||||
|
|
||||||
await page.route('**/*', async route => {
|
await page.route('**/*', async route => {
|
||||||
// @ts-expect-error
|
|
||||||
await route._continueToResponse({});
|
|
||||||
await route.fulfill({
|
await route.fulfill({
|
||||||
// @ts-expect-error
|
|
||||||
response: sampleResponse,
|
response: sampleResponse,
|
||||||
status: 201,
|
status: 201,
|
||||||
contentType: 'text/plain'
|
contentType: 'text/plain'
|
||||||
|
|
@ -104,28 +97,10 @@ it('should fulfill with any response', async ({ page, server, browserName, brows
|
||||||
expect(response.headers()['foo']).toBe('bar');
|
expect(response.headers()['foo']).toBe('bar');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw on continue after intercept', async ({ page, server, browserName }) => {
|
|
||||||
let routeCallback;
|
|
||||||
const routePromise = new Promise<Route>(f => routeCallback = f);
|
|
||||||
await page.route('**', routeCallback);
|
|
||||||
|
|
||||||
page.goto(server.EMPTY_PAGE).catch(e => {});
|
|
||||||
const route = await routePromise;
|
|
||||||
// @ts-expect-error
|
|
||||||
await route._continueToResponse();
|
|
||||||
try {
|
|
||||||
await route.continue();
|
|
||||||
fail('did not throw');
|
|
||||||
} catch (e) {
|
|
||||||
expect(e.message).toContain('Cannot call continue after response interception!');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should support fulfill after intercept', async ({ page, server }) => {
|
it('should support fulfill after intercept', async ({ page, server }) => {
|
||||||
const requestPromise = server.waitForRequest('/title.html');
|
const requestPromise = server.waitForRequest('/title.html');
|
||||||
await page.route('**', async route => {
|
await page.route('**', async route => {
|
||||||
// @ts-expect-error
|
const response = await page.request.fetch(route.request());
|
||||||
const response = await route._continueToResponse();
|
|
||||||
await route.fulfill({ response });
|
await route.fulfill({ response });
|
||||||
});
|
});
|
||||||
const response = await page.goto(server.PREFIX + '/title.html');
|
const response = await page.goto(server.PREFIX + '/title.html');
|
||||||
|
|
@ -134,52 +109,6 @@ it('should support fulfill after intercept', async ({ page, server }) => {
|
||||||
expect(await response.text()).toBe('<title>Woof-Woof</title>' + os.EOL);
|
expect(await response.text()).toBe('<title>Woof-Woof</title>' + os.EOL);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should intercept failures', async ({ page, browserName, browserMajorVersion, server }) => {
|
|
||||||
it.skip(browserName === 'chromium' && browserMajorVersion <= 91, 'Fails in Electron that uses old Chromium');
|
|
||||||
server.setRoute('/title.html', (req, res) => {
|
|
||||||
req.destroy();
|
|
||||||
});
|
|
||||||
const requestPromise = server.waitForRequest('/title.html');
|
|
||||||
let error;
|
|
||||||
await page.route('**', async route => {
|
|
||||||
try {
|
|
||||||
// @ts-expect-error
|
|
||||||
const response = await route._continueToResponse();
|
|
||||||
await route.fulfill({ response });
|
|
||||||
} catch (e) {
|
|
||||||
error = e;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const [request] = await Promise.all([
|
|
||||||
requestPromise,
|
|
||||||
page.goto(server.PREFIX + '/title.html').catch(e => {})
|
|
||||||
]);
|
|
||||||
expect(error).toBeTruthy();
|
|
||||||
expect(error.message).toContain('Request failed');
|
|
||||||
expect(request.url).toBe('/title.html');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should support request overrides', async ({ page, server, browserName, browserMajorVersion }) => {
|
|
||||||
it.skip(browserName === 'chromium' && browserMajorVersion <= 91, 'Fails in Electron that uses old Chromium');
|
|
||||||
const requestPromise = server.waitForRequest('/empty.html');
|
|
||||||
await page.route('**/foo', async route => {
|
|
||||||
// @ts-expect-error
|
|
||||||
const response = await route._continueToResponse({
|
|
||||||
url: server.EMPTY_PAGE,
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'foo': 'bar' },
|
|
||||||
postData: 'my data',
|
|
||||||
});
|
|
||||||
await route.fulfill({ response });
|
|
||||||
});
|
|
||||||
await page.goto(server.PREFIX + '/foo');
|
|
||||||
const request = await requestPromise;
|
|
||||||
expect(request.method).toBe('POST');
|
|
||||||
expect(request.url).toBe('/empty.html');
|
|
||||||
expect(request.headers['foo']).toBe('bar');
|
|
||||||
expect((await request.postBody).toString('utf8')).toBe('my data');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should give access to the intercepted response', async ({ page, server }) => {
|
it('should give access to the intercepted response', async ({ page, server }) => {
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
|
||||||
|
|
@ -190,42 +119,18 @@ it('should give access to the intercepted response', async ({ page, server }) =>
|
||||||
const evalPromise = page.evaluate(url => fetch(url), server.PREFIX + '/title.html');
|
const evalPromise = page.evaluate(url => fetch(url), server.PREFIX + '/title.html');
|
||||||
|
|
||||||
const route = await routePromise;
|
const route = await routePromise;
|
||||||
// @ts-expect-error
|
const response = await page.request.fetch(route.request());
|
||||||
const response: Response = await route._continueToResponse();
|
|
||||||
|
|
||||||
expect(response.status()).toBe(200);
|
expect(response.status()).toBe(200);
|
||||||
|
expect(response.statusText()).toBe('OK');
|
||||||
expect(response.ok()).toBeTruthy();
|
expect(response.ok()).toBeTruthy();
|
||||||
expect(response.url()).toBe(server.PREFIX + '/title.html');
|
expect(response.url()).toBe(server.PREFIX + '/title.html');
|
||||||
expect(response.headers()['content-type']).toBe('text/html; charset=utf-8');
|
expect(response.headers()['content-type']).toBe('text/html; charset=utf-8');
|
||||||
expect((await response.allHeaders())['content-type']).toBe('text/html; charset=utf-8');
|
|
||||||
expect(await (await response.headersArray()).filter(({ name }) => name.toLowerCase() === 'content-type')).toEqual([{ name: 'Content-Type', value: 'text/html; charset=utf-8' }]);
|
expect(await (await response.headersArray()).filter(({ name }) => name.toLowerCase() === 'content-type')).toEqual([{ name: 'Content-Type', value: 'text/html; charset=utf-8' }]);
|
||||||
|
|
||||||
// @ts-expect-error
|
|
||||||
await Promise.all([route.fulfill({ response }), evalPromise]);
|
await Promise.all([route.fulfill({ response }), evalPromise]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should give access to the intercepted response status text', async ({ page, server, browserName }) => {
|
|
||||||
it.fail(browserName === 'chromium', 'Status line is not reported for intercepted responses');
|
|
||||||
await page.goto(server.EMPTY_PAGE);
|
|
||||||
server.setRoute('/title.html', (req, res) => {
|
|
||||||
res.statusCode = 200;
|
|
||||||
res.statusMessage = 'You are awesome';
|
|
||||||
res.setHeader('Content-Type', 'text/plain');
|
|
||||||
res.end();
|
|
||||||
});
|
|
||||||
let routeCallback;
|
|
||||||
const routePromise = new Promise<Route>(f => routeCallback = f);
|
|
||||||
await page.route('**/title.html', routeCallback);
|
|
||||||
const evalPromise = page.evaluate(url => fetch(url), server.PREFIX + '/title.html');
|
|
||||||
const route = await routePromise;
|
|
||||||
// @ts-expect-error
|
|
||||||
const response = await route._continueToResponse();
|
|
||||||
|
|
||||||
await Promise.all([route.fulfill({ response }), evalPromise]);
|
|
||||||
expect(response.statusText()).toBe('You are awesome');
|
|
||||||
expect(response.url()).toBe(server.PREFIX + '/title.html');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should give access to the intercepted response body', async ({ page, server }) => {
|
it('should give access to the intercepted response body', async ({ page, server }) => {
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
|
||||||
|
|
@ -236,139 +141,9 @@ it('should give access to the intercepted response body', async ({ page, server
|
||||||
const evalPromise = page.evaluate(url => fetch(url), server.PREFIX + '/simple.json').catch(() => {});
|
const evalPromise = page.evaluate(url => fetch(url), server.PREFIX + '/simple.json').catch(() => {});
|
||||||
|
|
||||||
const route = await routePromise;
|
const route = await routePromise;
|
||||||
// @ts-expect-error
|
const response = await page.request.fetch(route.request());
|
||||||
const response = await route._continueToResponse();
|
|
||||||
|
|
||||||
expect((await response.text())).toBe('{"foo": "bar"}\n');
|
expect((await response.text())).toBe('{"foo": "bar"}\n');
|
||||||
|
|
||||||
await Promise.all([route.fulfill({ response }), evalPromise]);
|
await Promise.all([route.fulfill({ response }), evalPromise]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be abortable after interception', async ({ page, server, browserName }) => {
|
|
||||||
await page.route(/\.css$/, async route => {
|
|
||||||
// @ts-expect-error
|
|
||||||
await route._continueToResponse();
|
|
||||||
await route.abort();
|
|
||||||
});
|
|
||||||
let failed = false;
|
|
||||||
page.on('requestfailed', request => {
|
|
||||||
if (request.url().includes('.css'))
|
|
||||||
failed = true;
|
|
||||||
});
|
|
||||||
const response = await page.goto(server.PREFIX + '/one-style.html');
|
|
||||||
expect(response.ok()).toBe(true);
|
|
||||||
expect(response.request().failure()).toBe(null);
|
|
||||||
expect(failed).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should fulfill after redirects', async ({ page, server, browserName }) => {
|
|
||||||
server.setRedirect('/redirect/1.html', '/redirect/2.html');
|
|
||||||
server.setRedirect('/redirect/2.html', '/empty.html');
|
|
||||||
const expectedUrls = ['/redirect/1.html', '/redirect/2.html', '/empty.html'].map(s => server.PREFIX + s);
|
|
||||||
const requestUrls = [];
|
|
||||||
const responseUrls = [];
|
|
||||||
const requestFinishedUrls = [];
|
|
||||||
page.on('request', request => requestUrls.push(request.url()));
|
|
||||||
page.on('response', response => responseUrls.push(response.url()));
|
|
||||||
page.on('requestfinished', request => requestFinishedUrls.push(request.url()));
|
|
||||||
let routeCalls = 0;
|
|
||||||
await page.route('**/*', async route => {
|
|
||||||
++routeCalls;
|
|
||||||
// @ts-expect-error
|
|
||||||
await route._continueToResponse({});
|
|
||||||
await route.fulfill({
|
|
||||||
status: 201,
|
|
||||||
headers: {
|
|
||||||
foo: 'bar'
|
|
||||||
},
|
|
||||||
contentType: 'text/plain',
|
|
||||||
body: 'Yo, page!'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
const response = await page.goto(server.PREFIX + '/redirect/1.html');
|
|
||||||
expect(requestUrls).toEqual(expectedUrls);
|
|
||||||
expect(responseUrls).toEqual(expectedUrls);
|
|
||||||
await response.finished();
|
|
||||||
expect(requestFinishedUrls).toEqual(expectedUrls);
|
|
||||||
expect(routeCalls).toBe(1);
|
|
||||||
|
|
||||||
const redirectChain = [];
|
|
||||||
for (let req = response.request(); req; req = req.redirectedFrom())
|
|
||||||
redirectChain.unshift(req.url());
|
|
||||||
expect(redirectChain).toEqual(expectedUrls);
|
|
||||||
|
|
||||||
expect(response.status()).toBe(201);
|
|
||||||
expect(response.headers().foo).toBe('bar');
|
|
||||||
expect(response.headers()['content-type']).toBe('text/plain');
|
|
||||||
expect(await response.text()).toBe('Yo, page!');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should fulfill original response after redirects', async ({ page, browserName, server }) => {
|
|
||||||
server.setRedirect('/redirect/1.html', '/redirect/2.html');
|
|
||||||
server.setRedirect('/redirect/2.html', '/title.html');
|
|
||||||
const expectedUrls = ['/redirect/1.html', '/redirect/2.html', '/title.html'].map(s => server.PREFIX + s);
|
|
||||||
const requestUrls = [];
|
|
||||||
const responseUrls = [];
|
|
||||||
const requestFinishedUrls = [];
|
|
||||||
page.on('request', request => requestUrls.push(request.url()));
|
|
||||||
page.on('response', response => responseUrls.push(response.url()));
|
|
||||||
page.on('requestfinished', request => requestFinishedUrls.push(request.url()));
|
|
||||||
let routeCalls = 0;
|
|
||||||
await page.route('**/*', async route => {
|
|
||||||
++routeCalls;
|
|
||||||
// @ts-expect-error
|
|
||||||
const response = await route._continueToResponse({});
|
|
||||||
await route.fulfill({ response });
|
|
||||||
});
|
|
||||||
const response = await page.goto(server.PREFIX + '/redirect/1.html');
|
|
||||||
expect(requestUrls).toEqual(expectedUrls);
|
|
||||||
expect(responseUrls).toEqual(expectedUrls);
|
|
||||||
await response.finished();
|
|
||||||
expect(requestFinishedUrls).toEqual(expectedUrls);
|
|
||||||
expect(routeCalls).toBe(1);
|
|
||||||
|
|
||||||
const redirectChain = [];
|
|
||||||
for (let req = response.request(); req; req = req.redirectedFrom())
|
|
||||||
redirectChain.unshift(req.url());
|
|
||||||
expect(redirectChain).toEqual(expectedUrls);
|
|
||||||
|
|
||||||
expect(response.status()).toBe(200);
|
|
||||||
expect(await response.text()).toBe('<title>Woof-Woof</title>' + os.EOL);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should abort after redirects', async ({ page, browserName, server }) => {
|
|
||||||
server.setRedirect('/redirect/1.html', '/redirect/2.html');
|
|
||||||
server.setRedirect('/redirect/2.html', '/title.html');
|
|
||||||
const expectedUrls = ['/redirect/1.html', '/redirect/2.html', '/title.html'].map(s => server.PREFIX + s);
|
|
||||||
const requestUrls = [];
|
|
||||||
const responseUrls = [];
|
|
||||||
const requestFinishedUrls = [];
|
|
||||||
const requestFailedUrls = [];
|
|
||||||
page.on('request', request => requestUrls.push(request.url()));
|
|
||||||
page.on('response', response => responseUrls.push(response.url()));
|
|
||||||
page.on('requestfinished', request => requestFinishedUrls.push(request.url()));
|
|
||||||
page.on('requestfailed', request => requestFailedUrls.push(request.url()));
|
|
||||||
let routeCalls = 0;
|
|
||||||
await page.route('**/*', async route => {
|
|
||||||
++routeCalls;
|
|
||||||
// @ts-expect-error
|
|
||||||
await route._continueToResponse({});
|
|
||||||
await route.abort('connectionreset');
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
await page.goto(server.PREFIX + '/redirect/1.html');
|
|
||||||
} catch (e) {
|
|
||||||
if (browserName === 'webkit')
|
|
||||||
expect(e.message).toContain('Request intercepted');
|
|
||||||
else if (browserName === 'chromium')
|
|
||||||
expect(e.message).toContain('ERR_CONNECTION_RESET');
|
|
||||||
else
|
|
||||||
expect(e.message).toContain('NS_ERROR_NET_RESET');
|
|
||||||
}
|
|
||||||
expect(requestUrls).toEqual(expectedUrls);
|
|
||||||
expect(responseUrls).toEqual(expectedUrls.slice(0, -1));
|
|
||||||
expect(requestFinishedUrls).toEqual(expectedUrls.slice(0, -1));
|
|
||||||
expect(requestFailedUrls).toEqual(expectedUrls.slice(-1));
|
|
||||||
expect(routeCalls).toBe(1);
|
|
||||||
});
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue