parent
4c3caaa3f9
commit
ac162c561e
@ -59,7 +59,7 @@
|
|||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "4.3.2",
|
"debug": "4.3.2",
|
||||||
"devtools-protocol": "0.0.901419",
|
"devtools-protocol": "0.0.937139",
|
||||||
"extract-zip": "2.0.1",
|
"extract-zip": "2.0.1",
|
||||||
"https-proxy-agent": "5.0.0",
|
"https-proxy-agent": "5.0.0",
|
||||||
"node-fetch": "2.6.5",
|
"node-fetch": "2.6.5",
|
||||||
|
@ -199,7 +199,7 @@ export class FrameManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
watcher.dispose();
|
watcher.dispose();
|
||||||
if (error) throw error;
|
if (error) throw error;
|
||||||
return watcher.navigationResponse();
|
return await watcher.navigationResponse();
|
||||||
|
|
||||||
async function navigate(
|
async function navigate(
|
||||||
client: CDPSession,
|
client: CDPSession,
|
||||||
@ -243,7 +243,7 @@ export class FrameManager extends EventEmitter {
|
|||||||
]);
|
]);
|
||||||
watcher.dispose();
|
watcher.dispose();
|
||||||
if (error) throw error;
|
if (error) throw error;
|
||||||
return watcher.navigationResponse();
|
return await watcher.navigationResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _onAttachedToTarget(
|
private async _onAttachedToTarget(
|
||||||
|
@ -13,7 +13,9 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { CDPSession } from './Connection.js';
|
import { ProtocolMapping } from 'devtools-protocol/types/protocol-mapping.js';
|
||||||
|
|
||||||
|
import { EventEmitter } from './EventEmitter.js';
|
||||||
import { Frame } from './FrameManager.js';
|
import { Frame } from './FrameManager.js';
|
||||||
import { HTTPResponse } from './HTTPResponse.js';
|
import { HTTPResponse } from './HTTPResponse.js';
|
||||||
import { assert } from './assert.js';
|
import { assert } from './assert.js';
|
||||||
@ -56,6 +58,13 @@ export interface ResponseForRequest {
|
|||||||
*/
|
*/
|
||||||
export type ResourceType = Lowercase<Protocol.Network.ResourceType>;
|
export type ResourceType = Lowercase<Protocol.Network.ResourceType>;
|
||||||
|
|
||||||
|
interface CDPSession extends EventEmitter {
|
||||||
|
send<T extends keyof ProtocolMapping.Commands>(
|
||||||
|
method: T,
|
||||||
|
...paramArgs: ProtocolMapping.Commands[T]['paramsType']
|
||||||
|
): Promise<ProtocolMapping.Commands[T]['returnType']>;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Represents an HTTP request sent by a page.
|
* Represents an HTTP request sent by a page.
|
||||||
|
@ -13,7 +13,9 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { CDPSession } from './Connection.js';
|
import { ProtocolMapping } from 'devtools-protocol/types/protocol-mapping.js';
|
||||||
|
|
||||||
|
import { EventEmitter } from './EventEmitter.js';
|
||||||
import { Frame } from './FrameManager.js';
|
import { Frame } from './FrameManager.js';
|
||||||
import { HTTPRequest } from './HTTPRequest.js';
|
import { HTTPRequest } from './HTTPRequest.js';
|
||||||
import { SecurityDetails } from './SecurityDetails.js';
|
import { SecurityDetails } from './SecurityDetails.js';
|
||||||
@ -28,6 +30,13 @@ export interface RemoteAddress {
|
|||||||
port: number;
|
port: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface CDPSession extends EventEmitter {
|
||||||
|
send<T extends keyof ProtocolMapping.Commands>(
|
||||||
|
method: T,
|
||||||
|
...paramArgs: ProtocolMapping.Commands[T]['paramsType']
|
||||||
|
): Promise<ProtocolMapping.Commands[T]['returnType']>;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The HTTPResponse class represents responses which are received by the
|
* The HTTPResponse class represents responses which are received by the
|
||||||
* {@link Page} class.
|
* {@link Page} class.
|
||||||
@ -55,7 +64,8 @@ export class HTTPResponse {
|
|||||||
constructor(
|
constructor(
|
||||||
client: CDPSession,
|
client: CDPSession,
|
||||||
request: HTTPRequest,
|
request: HTTPRequest,
|
||||||
responsePayload: Protocol.Network.Response
|
responsePayload: Protocol.Network.Response,
|
||||||
|
extraInfo: Protocol.Network.ResponseReceivedExtraInfoEvent | null
|
||||||
) {
|
) {
|
||||||
this._client = client;
|
this._client = client;
|
||||||
this._request = request;
|
this._request = request;
|
||||||
@ -68,13 +78,17 @@ export class HTTPResponse {
|
|||||||
ip: responsePayload.remoteIPAddress,
|
ip: responsePayload.remoteIPAddress,
|
||||||
port: responsePayload.remotePort,
|
port: responsePayload.remotePort,
|
||||||
};
|
};
|
||||||
this._status = responsePayload.status;
|
// TODO extract statusText from extraInfo.headersText instead if present
|
||||||
this._statusText = responsePayload.statusText;
|
this._statusText = responsePayload.statusText;
|
||||||
this._url = request.url();
|
this._url = request.url();
|
||||||
this._fromDiskCache = !!responsePayload.fromDiskCache;
|
this._fromDiskCache = !!responsePayload.fromDiskCache;
|
||||||
this._fromServiceWorker = !!responsePayload.fromServiceWorker;
|
this._fromServiceWorker = !!responsePayload.fromServiceWorker;
|
||||||
for (const key of Object.keys(responsePayload.headers))
|
|
||||||
this._headers[key.toLowerCase()] = responsePayload.headers[key];
|
this._status = extraInfo ? extraInfo.statusCode : responsePayload.status;
|
||||||
|
const headers = extraInfo ? extraInfo.headers : responsePayload.headers;
|
||||||
|
for (const key of Object.keys(headers))
|
||||||
|
this._headers[key.toLowerCase()] = headers[key];
|
||||||
|
|
||||||
this._securityDetails = responsePayload.securityDetails
|
this._securityDetails = responsePayload.securityDetails
|
||||||
? new SecurityDetails(responsePayload.securityDetails)
|
? new SecurityDetails(responsePayload.securityDetails)
|
||||||
: null;
|
: null;
|
||||||
|
@ -171,7 +171,8 @@ export class LifecycleWatcher {
|
|||||||
this._checkLifecycleComplete();
|
this._checkLifecycleComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
navigationResponse(): HTTPResponse | null {
|
async navigationResponse(): Promise<HTTPResponse | null> {
|
||||||
|
// We may need to wait for ExtraInfo events before the request is complete.
|
||||||
return this._navigationRequest ? this._navigationRequest.response() : null;
|
return this._navigationRequest ? this._navigationRequest.response() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,12 +13,13 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { ProtocolMapping } from 'devtools-protocol/types/protocol-mapping.js';
|
||||||
import { EventEmitter } from './EventEmitter.js';
|
import { EventEmitter } from './EventEmitter.js';
|
||||||
|
import { Frame } from './FrameManager.js';
|
||||||
import { assert } from './assert.js';
|
import { assert } from './assert.js';
|
||||||
import { helper, debugError } from './helper.js';
|
import { helper, debugError } from './helper.js';
|
||||||
import { Protocol } from 'devtools-protocol';
|
import { Protocol } from 'devtools-protocol';
|
||||||
import { CDPSession } from './Connection.js';
|
|
||||||
import { FrameManager } from './FrameManager.js';
|
|
||||||
import { HTTPRequest } from './HTTPRequest.js';
|
import { HTTPRequest } from './HTTPRequest.js';
|
||||||
import { HTTPResponse } from './HTTPResponse.js';
|
import { HTTPResponse } from './HTTPResponse.js';
|
||||||
|
|
||||||
@ -62,6 +63,17 @@ export const NetworkManagerEmittedEvents = {
|
|||||||
RequestFinished: Symbol('NetworkManager.RequestFinished'),
|
RequestFinished: Symbol('NetworkManager.RequestFinished'),
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
interface CDPSession extends EventEmitter {
|
||||||
|
send<T extends keyof ProtocolMapping.Commands>(
|
||||||
|
method: T,
|
||||||
|
...paramArgs: ProtocolMapping.Commands[T]['paramsType']
|
||||||
|
): Promise<ProtocolMapping.Commands[T]['returnType']>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FrameManager {
|
||||||
|
frame(frameId: string): Frame | null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
@ -110,6 +122,37 @@ export class NetworkManager extends EventEmitter {
|
|||||||
>();
|
>();
|
||||||
_requestIdToRequest = new Map<string, HTTPRequest>();
|
_requestIdToRequest = new Map<string, HTTPRequest>();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The below maps are used to reconcile Network.responseReceivedExtraInfo
|
||||||
|
* events with their corresponding request. Each response and redirect
|
||||||
|
* response gets an ExtraInfo event, and we don't know which will come first.
|
||||||
|
* This means that we have to store a Response or an ExtraInfo for each
|
||||||
|
* response, and emit the event when we get both of them. In addition, to
|
||||||
|
* handle redirects, we have to make them Arrays to represent the chain of
|
||||||
|
* events.
|
||||||
|
*/
|
||||||
|
_requestIdToResponseReceivedExtraInfo = new Map<
|
||||||
|
string,
|
||||||
|
Protocol.Network.ResponseReceivedExtraInfoEvent[]
|
||||||
|
>();
|
||||||
|
_requestIdToQueuedRedirectInfoMap = new Map<
|
||||||
|
string,
|
||||||
|
Array<{
|
||||||
|
event: Protocol.Network.RequestWillBeSentEvent;
|
||||||
|
interceptionId?: string;
|
||||||
|
}>
|
||||||
|
>();
|
||||||
|
_requestIdToQueuedEvents = new Map<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
responseReceived: Protocol.Network.ResponseReceivedEvent;
|
||||||
|
promise: Promise<void>;
|
||||||
|
resolver: () => void;
|
||||||
|
loadingFinished?: Protocol.Network.LoadingFinishedEvent;
|
||||||
|
loadingFailed?: Protocol.Network.LoadingFailedEvent;
|
||||||
|
}
|
||||||
|
>();
|
||||||
|
|
||||||
_extraHTTPHeaders: Record<string, string> = {};
|
_extraHTTPHeaders: Record<string, string> = {};
|
||||||
_credentials?: Credentials = null;
|
_credentials?: Credentials = null;
|
||||||
_attemptedAuthentications = new Set<string>();
|
_attemptedAuthentications = new Set<string>();
|
||||||
@ -152,6 +195,10 @@ export class NetworkManager extends EventEmitter {
|
|||||||
this._onLoadingFinished.bind(this)
|
this._onLoadingFinished.bind(this)
|
||||||
);
|
);
|
||||||
this._client.on('Network.loadingFailed', this._onLoadingFailed.bind(this));
|
this._client.on('Network.loadingFailed', this._onLoadingFailed.bind(this));
|
||||||
|
this._client.on(
|
||||||
|
'Network.responseReceivedExtraInfo',
|
||||||
|
this._onResponseReceivedExtraInfo.bind(this)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async initialize(): Promise<void> {
|
async initialize(): Promise<void> {
|
||||||
@ -361,17 +408,61 @@ export class NetworkManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_requestIdToQueuedRedirectInfo(requestId: string): Array<{
|
||||||
|
event: Protocol.Network.RequestWillBeSentEvent;
|
||||||
|
interceptionId?: string;
|
||||||
|
}> {
|
||||||
|
if (!this._requestIdToQueuedRedirectInfoMap.has(requestId)) {
|
||||||
|
this._requestIdToQueuedRedirectInfoMap.set(requestId, []);
|
||||||
|
}
|
||||||
|
return this._requestIdToQueuedRedirectInfoMap.get(requestId);
|
||||||
|
}
|
||||||
|
|
||||||
|
_requestIdToResponseExtraInfo(
|
||||||
|
requestId: string
|
||||||
|
): Protocol.Network.ResponseReceivedExtraInfoEvent[] {
|
||||||
|
if (!this._requestIdToResponseReceivedExtraInfo.has(requestId)) {
|
||||||
|
this._requestIdToResponseReceivedExtraInfo.set(requestId, []);
|
||||||
|
}
|
||||||
|
return this._requestIdToResponseReceivedExtraInfo.get(requestId);
|
||||||
|
}
|
||||||
|
|
||||||
_onRequest(
|
_onRequest(
|
||||||
event: Protocol.Network.RequestWillBeSentEvent,
|
event: Protocol.Network.RequestWillBeSentEvent,
|
||||||
interceptionId?: string
|
interceptionId?: string
|
||||||
): void {
|
): void {
|
||||||
let redirectChain = [];
|
let redirectChain = [];
|
||||||
if (event.redirectResponse) {
|
if (event.redirectResponse) {
|
||||||
|
// We want to emit a response and requestfinished for the
|
||||||
|
// redirectResponse, but we can't do so unless we have a
|
||||||
|
// responseExtraInfo ready to pair it up with. If we don't have any
|
||||||
|
// responseExtraInfos saved in our queue, they we have to wait until
|
||||||
|
// the next one to emit response and requestfinished, *and* we should
|
||||||
|
// also wait to emit this Request too because it should come after the
|
||||||
|
// response/requestfinished.
|
||||||
|
let redirectResponseExtraInfo = null;
|
||||||
|
if (event.redirectHasExtraInfo) {
|
||||||
|
redirectResponseExtraInfo = this._requestIdToResponseExtraInfo(
|
||||||
|
event.requestId
|
||||||
|
).shift();
|
||||||
|
if (!redirectResponseExtraInfo) {
|
||||||
|
this._requestIdToQueuedRedirectInfo(event.requestId).push({
|
||||||
|
event,
|
||||||
|
interceptionId,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const request = this._requestIdToRequest.get(event.requestId);
|
const request = this._requestIdToRequest.get(event.requestId);
|
||||||
// If we connect late to the target, we could have missed the
|
// If we connect late to the target, we could have missed the
|
||||||
// requestWillBeSent event.
|
// requestWillBeSent event.
|
||||||
if (request) {
|
if (request) {
|
||||||
this._handleRequestRedirect(request, event.redirectResponse);
|
this._handleRequestRedirect(
|
||||||
|
request,
|
||||||
|
event.redirectResponse,
|
||||||
|
redirectResponseExtraInfo
|
||||||
|
);
|
||||||
redirectChain = request._redirectChain;
|
redirectChain = request._redirectChain;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -401,9 +492,15 @@ export class NetworkManager extends EventEmitter {
|
|||||||
|
|
||||||
_handleRequestRedirect(
|
_handleRequestRedirect(
|
||||||
request: HTTPRequest,
|
request: HTTPRequest,
|
||||||
responsePayload: Protocol.Network.Response
|
responsePayload: Protocol.Network.Response,
|
||||||
|
extraInfo: Protocol.Network.ResponseReceivedExtraInfoEvent
|
||||||
): void {
|
): void {
|
||||||
const response = new HTTPResponse(this._client, request, responsePayload);
|
const response = new HTTPResponse(
|
||||||
|
this._client,
|
||||||
|
request,
|
||||||
|
responsePayload,
|
||||||
|
extraInfo
|
||||||
|
);
|
||||||
request._response = response;
|
request._response = response;
|
||||||
request._redirectChain.push(request);
|
request._redirectChain.push(request);
|
||||||
response._resolveBody(
|
response._resolveBody(
|
||||||
@ -414,15 +511,93 @@ export class NetworkManager extends EventEmitter {
|
|||||||
this.emit(NetworkManagerEmittedEvents.RequestFinished, request);
|
this.emit(NetworkManagerEmittedEvents.RequestFinished, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onResponseReceived(event: Protocol.Network.ResponseReceivedEvent): void {
|
_emitResponseEvent(
|
||||||
const request = this._requestIdToRequest.get(event.requestId);
|
responseReceived: Protocol.Network.ResponseReceivedEvent,
|
||||||
|
extraInfo: Protocol.Network.ResponseReceivedExtraInfoEvent
|
||||||
|
): void {
|
||||||
|
const request = this._requestIdToRequest.get(responseReceived.requestId);
|
||||||
// FileUpload sends a response without a matching request.
|
// FileUpload sends a response without a matching request.
|
||||||
if (!request) return;
|
if (!request) return;
|
||||||
const response = new HTTPResponse(this._client, request, event.response);
|
|
||||||
|
const extraInfos = this._requestIdToResponseExtraInfo(
|
||||||
|
responseReceived.requestId
|
||||||
|
);
|
||||||
|
if (extraInfos.length) {
|
||||||
|
throw new Error(
|
||||||
|
'Unexpected extraInfo events for request ' + responseReceived.requestId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = new HTTPResponse(
|
||||||
|
this._client,
|
||||||
|
request,
|
||||||
|
responseReceived.response,
|
||||||
|
extraInfo
|
||||||
|
);
|
||||||
request._response = response;
|
request._response = response;
|
||||||
this.emit(NetworkManagerEmittedEvents.Response, response);
|
this.emit(NetworkManagerEmittedEvents.Response, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onResponseReceived(event: Protocol.Network.ResponseReceivedEvent): void {
|
||||||
|
const request = this._requestIdToRequest.get(event.requestId);
|
||||||
|
let extraInfo = null;
|
||||||
|
if (request && !request._fromMemoryCache && event.hasExtraInfo) {
|
||||||
|
extraInfo = this._requestIdToResponseExtraInfo(event.requestId).shift();
|
||||||
|
if (!extraInfo) {
|
||||||
|
// Wait until we get the corresponding ExtraInfo event.
|
||||||
|
let resolver = null;
|
||||||
|
const promise = new Promise<void>((resolve) => (resolver = resolve));
|
||||||
|
this._requestIdToQueuedEvents.set(event.requestId, {
|
||||||
|
responseReceived: event,
|
||||||
|
promise,
|
||||||
|
resolver,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._emitResponseEvent(event, extraInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
responseWaitingForExtraInfoPromise(requestId: string): Promise<void> {
|
||||||
|
const responseReceived = this._requestIdToQueuedEvents.get(requestId);
|
||||||
|
if (!responseReceived) return Promise.resolve();
|
||||||
|
return responseReceived.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
_onResponseReceivedExtraInfo(
|
||||||
|
event: Protocol.Network.ResponseReceivedExtraInfoEvent
|
||||||
|
): void {
|
||||||
|
// We may have skipped a redirect response/request pair due to waiting for
|
||||||
|
// this ExtraInfo event. If so, continue that work now that we have the
|
||||||
|
// request.
|
||||||
|
const redirectInfo = this._requestIdToQueuedRedirectInfo(
|
||||||
|
event.requestId
|
||||||
|
).shift();
|
||||||
|
if (redirectInfo) {
|
||||||
|
this._requestIdToResponseExtraInfo(event.requestId).push(event);
|
||||||
|
this._onRequest(redirectInfo.event, redirectInfo.interceptionId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We may have skipped response and loading events because we didn't have
|
||||||
|
// this ExtraInfo event yet. If so, emit those events now.
|
||||||
|
const queuedEvents = this._requestIdToQueuedEvents.get(event.requestId);
|
||||||
|
if (queuedEvents) {
|
||||||
|
this._emitResponseEvent(queuedEvents.responseReceived, event);
|
||||||
|
if (queuedEvents.loadingFinished) {
|
||||||
|
this._emitLoadingFinished(queuedEvents.loadingFinished);
|
||||||
|
}
|
||||||
|
if (queuedEvents.loadingFailed) {
|
||||||
|
this._emitLoadingFailed(queuedEvents.loadingFailed);
|
||||||
|
}
|
||||||
|
queuedEvents.resolver();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until we get another event that can use this ExtraInfo event.
|
||||||
|
this._requestIdToResponseExtraInfo(event.requestId).push(event);
|
||||||
|
}
|
||||||
|
|
||||||
_forgetRequest(request: HTTPRequest, events: boolean): void {
|
_forgetRequest(request: HTTPRequest, events: boolean): void {
|
||||||
const requestId = request._requestId;
|
const requestId = request._requestId;
|
||||||
const interceptionId = request._interceptionId;
|
const interceptionId = request._interceptionId;
|
||||||
@ -433,10 +608,24 @@ export class NetworkManager extends EventEmitter {
|
|||||||
if (events) {
|
if (events) {
|
||||||
this._requestIdToRequestWillBeSentEvent.delete(requestId);
|
this._requestIdToRequestWillBeSentEvent.delete(requestId);
|
||||||
this._requestIdToRequestPausedEvent.delete(requestId);
|
this._requestIdToRequestPausedEvent.delete(requestId);
|
||||||
|
this._requestIdToQueuedEvents.delete(requestId);
|
||||||
|
this._requestIdToQueuedRedirectInfoMap.delete(requestId);
|
||||||
|
this._requestIdToResponseReceivedExtraInfo.delete(requestId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onLoadingFinished(event: Protocol.Network.LoadingFinishedEvent): void {
|
_onLoadingFinished(event: Protocol.Network.LoadingFinishedEvent): void {
|
||||||
|
// If the response event for this request is still waiting on a
|
||||||
|
// corresponding ExtraInfo event, then wait to emit this event too.
|
||||||
|
const queuedEvents = this._requestIdToQueuedEvents.get(event.requestId);
|
||||||
|
if (queuedEvents) {
|
||||||
|
queuedEvents.loadingFinished = event;
|
||||||
|
} else {
|
||||||
|
this._emitLoadingFinished(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_emitLoadingFinished(event: Protocol.Network.LoadingFinishedEvent): void {
|
||||||
const request = this._requestIdToRequest.get(event.requestId);
|
const request = this._requestIdToRequest.get(event.requestId);
|
||||||
// For certain requestIds we never receive requestWillBeSent event.
|
// For certain requestIds we never receive requestWillBeSent event.
|
||||||
// @see https://crbug.com/750469
|
// @see https://crbug.com/750469
|
||||||
@ -450,6 +639,17 @@ export class NetworkManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_onLoadingFailed(event: Protocol.Network.LoadingFailedEvent): void {
|
_onLoadingFailed(event: Protocol.Network.LoadingFailedEvent): void {
|
||||||
|
// If the response event for this request is still waiting on a
|
||||||
|
// corresponding ExtraInfo event, then wait to emit this event too.
|
||||||
|
const queuedEvents = this._requestIdToQueuedEvents.get(event.requestId);
|
||||||
|
if (queuedEvents) {
|
||||||
|
queuedEvents.loadingFailed = event;
|
||||||
|
} else {
|
||||||
|
this._emitLoadingFailed(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_emitLoadingFailed(event: Protocol.Network.LoadingFailedEvent): void {
|
||||||
const request = this._requestIdToRequest.get(event.requestId);
|
const request = this._requestIdToRequest.get(event.requestId);
|
||||||
// For certain requestIds we never receive requestWillBeSent event.
|
// For certain requestIds we never receive requestWillBeSent event.
|
||||||
// @see https://crbug.com/750469
|
// @see https://crbug.com/750469
|
||||||
|
@ -20,6 +20,6 @@ type Revisions = Readonly<{
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
export const PUPPETEER_REVISIONS: Revisions = {
|
export const PUPPETEER_REVISIONS: Revisions = {
|
||||||
chromium: '901912',
|
chromium: '938248',
|
||||||
firefox: 'latest',
|
firefox: 'latest',
|
||||||
};
|
};
|
||||||
|
459
test/NetworkManager.spec.ts
Normal file
459
test/NetworkManager.spec.ts
Normal file
@ -0,0 +1,459 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2020 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { describeChromeOnly } from './mocha-utils'; // eslint-disable-line import/extensions
|
||||||
|
|
||||||
|
import { NetworkManager } from '../lib/cjs/puppeteer/common/NetworkManager.js';
|
||||||
|
import { EventEmitter } from '../lib/cjs/puppeteer/common/EventEmitter.js';
|
||||||
|
import { Frame } from '../lib/cjs/puppeteer/common/FrameManager.js';
|
||||||
|
|
||||||
|
describeChromeOnly('NetworkManager', () => {
|
||||||
|
it('should process extra info on multiple redirects', async () => {
|
||||||
|
class MockCDPSession extends EventEmitter {
|
||||||
|
send(): any {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockCDPSession = new MockCDPSession();
|
||||||
|
new NetworkManager(mockCDPSession, true, {
|
||||||
|
frame(): Frame | null {
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
mockCDPSession.emit('Network.requestWillBeSent', {
|
||||||
|
requestId: '7760711DEFCFA23132D98ABA6B4E175C',
|
||||||
|
loaderId: '7760711DEFCFA23132D98ABA6B4E175C',
|
||||||
|
documentURL: 'http://localhost:8907/redirect/1.html',
|
||||||
|
request: {
|
||||||
|
url: 'http://localhost:8907/redirect/1.html',
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Upgrade-Insecure-Requests': '1',
|
||||||
|
'User-Agent':
|
||||||
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/97.0.4691.0 Safari/537.36',
|
||||||
|
},
|
||||||
|
mixedContentType: 'none',
|
||||||
|
initialPriority: 'VeryHigh',
|
||||||
|
referrerPolicy: 'strict-origin-when-cross-origin',
|
||||||
|
isSameSite: true,
|
||||||
|
},
|
||||||
|
timestamp: 2111.55635,
|
||||||
|
wallTime: 1637315638.473634,
|
||||||
|
initiator: { type: 'other' },
|
||||||
|
redirectHasExtraInfo: false,
|
||||||
|
type: 'Document',
|
||||||
|
frameId: '099A5216AF03AAFEC988F214B024DF08',
|
||||||
|
hasUserGesture: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
mockCDPSession.emit('Network.requestWillBeSentExtraInfo', {
|
||||||
|
requestId: '7760711DEFCFA23132D98ABA6B4E175C',
|
||||||
|
associatedCookies: [],
|
||||||
|
headers: {
|
||||||
|
Host: 'localhost:8907',
|
||||||
|
Connection: 'keep-alive',
|
||||||
|
'Upgrade-Insecure-Requests': '1',
|
||||||
|
'User-Agent':
|
||||||
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/97.0.4691.0 Safari/537.36',
|
||||||
|
Accept:
|
||||||
|
'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
|
||||||
|
'Sec-Fetch-Site': 'none',
|
||||||
|
'Sec-Fetch-Mode': 'navigate',
|
||||||
|
'Sec-Fetch-User': '?1',
|
||||||
|
'Sec-Fetch-Dest': 'document',
|
||||||
|
'Accept-Encoding': 'gzip, deflate, br',
|
||||||
|
},
|
||||||
|
connectTiming: { requestTime: 2111.557593 },
|
||||||
|
});
|
||||||
|
mockCDPSession.emit('Network.responseReceivedExtraInfo', {
|
||||||
|
requestId: '7760711DEFCFA23132D98ABA6B4E175C',
|
||||||
|
blockedCookies: [],
|
||||||
|
headers: {
|
||||||
|
location: '/redirect/2.html',
|
||||||
|
Date: 'Fri, 19 Nov 2021 09:53:58 GMT',
|
||||||
|
Connection: 'keep-alive',
|
||||||
|
'Keep-Alive': 'timeout=5',
|
||||||
|
'Transfer-Encoding': 'chunked',
|
||||||
|
},
|
||||||
|
resourceIPAddressSpace: 'Local',
|
||||||
|
statusCode: 302,
|
||||||
|
headersText:
|
||||||
|
'HTTP/1.1 302 Found\r\nlocation: /redirect/2.html\r\nDate: Fri, 19 Nov 2021 09:53:58 GMT\r\nConnection: keep-alive\r\nKeep-Alive: timeout=5\r\nTransfer-Encoding: chunked\r\n\r\n',
|
||||||
|
});
|
||||||
|
mockCDPSession.emit('Network.requestWillBeSent', {
|
||||||
|
requestId: '7760711DEFCFA23132D98ABA6B4E175C',
|
||||||
|
loaderId: '7760711DEFCFA23132D98ABA6B4E175C',
|
||||||
|
documentURL: 'http://localhost:8907/redirect/2.html',
|
||||||
|
request: {
|
||||||
|
url: 'http://localhost:8907/redirect/2.html',
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Upgrade-Insecure-Requests': '1',
|
||||||
|
'User-Agent':
|
||||||
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/97.0.4691.0 Safari/537.36',
|
||||||
|
},
|
||||||
|
mixedContentType: 'none',
|
||||||
|
initialPriority: 'VeryHigh',
|
||||||
|
referrerPolicy: 'strict-origin-when-cross-origin',
|
||||||
|
isSameSite: true,
|
||||||
|
},
|
||||||
|
timestamp: 2111.559124,
|
||||||
|
wallTime: 1637315638.47642,
|
||||||
|
initiator: { type: 'other' },
|
||||||
|
redirectHasExtraInfo: true,
|
||||||
|
redirectResponse: {
|
||||||
|
url: 'http://localhost:8907/redirect/1.html',
|
||||||
|
status: 302,
|
||||||
|
statusText: 'Found',
|
||||||
|
headers: {
|
||||||
|
location: '/redirect/2.html',
|
||||||
|
Date: 'Fri, 19 Nov 2021 09:53:58 GMT',
|
||||||
|
Connection: 'keep-alive',
|
||||||
|
'Keep-Alive': 'timeout=5',
|
||||||
|
'Transfer-Encoding': 'chunked',
|
||||||
|
},
|
||||||
|
mimeType: '',
|
||||||
|
connectionReused: false,
|
||||||
|
connectionId: 322,
|
||||||
|
remoteIPAddress: '[::1]',
|
||||||
|
remotePort: 8907,
|
||||||
|
fromDiskCache: false,
|
||||||
|
fromServiceWorker: false,
|
||||||
|
fromPrefetchCache: false,
|
||||||
|
encodedDataLength: 162,
|
||||||
|
timing: {
|
||||||
|
requestTime: 2111.557593,
|
||||||
|
proxyStart: -1,
|
||||||
|
proxyEnd: -1,
|
||||||
|
dnsStart: 0.241,
|
||||||
|
dnsEnd: 0.251,
|
||||||
|
connectStart: 0.251,
|
||||||
|
connectEnd: 0.47,
|
||||||
|
sslStart: -1,
|
||||||
|
sslEnd: -1,
|
||||||
|
workerStart: -1,
|
||||||
|
workerReady: -1,
|
||||||
|
workerFetchStart: -1,
|
||||||
|
workerRespondWithSettled: -1,
|
||||||
|
sendStart: 0.537,
|
||||||
|
sendEnd: 0.611,
|
||||||
|
pushStart: 0,
|
||||||
|
pushEnd: 0,
|
||||||
|
receiveHeadersEnd: 0.939,
|
||||||
|
},
|
||||||
|
responseTime: 1.637315638475744e12,
|
||||||
|
protocol: 'http/1.1',
|
||||||
|
securityState: 'secure',
|
||||||
|
},
|
||||||
|
type: 'Document',
|
||||||
|
frameId: '099A5216AF03AAFEC988F214B024DF08',
|
||||||
|
hasUserGesture: false,
|
||||||
|
});
|
||||||
|
mockCDPSession.emit('Network.requestWillBeSentExtraInfo', {
|
||||||
|
requestId: '7760711DEFCFA23132D98ABA6B4E175C',
|
||||||
|
associatedCookies: [],
|
||||||
|
headers: {
|
||||||
|
Host: 'localhost:8907',
|
||||||
|
Connection: 'keep-alive',
|
||||||
|
'Upgrade-Insecure-Requests': '1',
|
||||||
|
'User-Agent':
|
||||||
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/97.0.4691.0 Safari/537.36',
|
||||||
|
Accept:
|
||||||
|
'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
|
||||||
|
'Sec-Fetch-Site': 'none',
|
||||||
|
'Sec-Fetch-Mode': 'navigate',
|
||||||
|
'Sec-Fetch-User': '?1',
|
||||||
|
'Sec-Fetch-Dest': 'document',
|
||||||
|
'Accept-Encoding': 'gzip, deflate, br',
|
||||||
|
},
|
||||||
|
connectTiming: { requestTime: 2111.559346 },
|
||||||
|
});
|
||||||
|
mockCDPSession.emit('Network.requestWillBeSent', {
|
||||||
|
requestId: '7760711DEFCFA23132D98ABA6B4E175C',
|
||||||
|
loaderId: '7760711DEFCFA23132D98ABA6B4E175C',
|
||||||
|
documentURL: 'http://localhost:8907/redirect/3.html',
|
||||||
|
request: {
|
||||||
|
url: 'http://localhost:8907/redirect/3.html',
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Upgrade-Insecure-Requests': '1',
|
||||||
|
'User-Agent':
|
||||||
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/97.0.4691.0 Safari/537.36',
|
||||||
|
},
|
||||||
|
mixedContentType: 'none',
|
||||||
|
initialPriority: 'VeryHigh',
|
||||||
|
referrerPolicy: 'strict-origin-when-cross-origin',
|
||||||
|
isSameSite: true,
|
||||||
|
},
|
||||||
|
timestamp: 2111.560249,
|
||||||
|
wallTime: 1637315638.477543,
|
||||||
|
initiator: { type: 'other' },
|
||||||
|
redirectHasExtraInfo: true,
|
||||||
|
redirectResponse: {
|
||||||
|
url: 'http://localhost:8907/redirect/2.html',
|
||||||
|
status: 302,
|
||||||
|
statusText: 'Found',
|
||||||
|
headers: {
|
||||||
|
location: '/redirect/3.html',
|
||||||
|
Date: 'Fri, 19 Nov 2021 09:53:58 GMT',
|
||||||
|
Connection: 'keep-alive',
|
||||||
|
'Keep-Alive': 'timeout=5',
|
||||||
|
'Transfer-Encoding': 'chunked',
|
||||||
|
},
|
||||||
|
mimeType: '',
|
||||||
|
connectionReused: true,
|
||||||
|
connectionId: 322,
|
||||||
|
remoteIPAddress: '[::1]',
|
||||||
|
remotePort: 8907,
|
||||||
|
fromDiskCache: false,
|
||||||
|
fromServiceWorker: false,
|
||||||
|
fromPrefetchCache: false,
|
||||||
|
encodedDataLength: 162,
|
||||||
|
timing: {
|
||||||
|
requestTime: 2111.559346,
|
||||||
|
proxyStart: -1,
|
||||||
|
proxyEnd: -1,
|
||||||
|
dnsStart: -1,
|
||||||
|
dnsEnd: -1,
|
||||||
|
connectStart: -1,
|
||||||
|
connectEnd: -1,
|
||||||
|
sslStart: -1,
|
||||||
|
sslEnd: -1,
|
||||||
|
workerStart: -1,
|
||||||
|
workerReady: -1,
|
||||||
|
workerFetchStart: -1,
|
||||||
|
workerRespondWithSettled: -1,
|
||||||
|
sendStart: 0.15,
|
||||||
|
sendEnd: 0.196,
|
||||||
|
pushStart: 0,
|
||||||
|
pushEnd: 0,
|
||||||
|
receiveHeadersEnd: 0.507,
|
||||||
|
},
|
||||||
|
responseTime: 1.637315638477063e12,
|
||||||
|
protocol: 'http/1.1',
|
||||||
|
securityState: 'secure',
|
||||||
|
},
|
||||||
|
type: 'Document',
|
||||||
|
frameId: '099A5216AF03AAFEC988F214B024DF08',
|
||||||
|
hasUserGesture: false,
|
||||||
|
});
|
||||||
|
mockCDPSession.emit('Network.responseReceivedExtraInfo', {
|
||||||
|
requestId: '7760711DEFCFA23132D98ABA6B4E175C',
|
||||||
|
blockedCookies: [],
|
||||||
|
headers: {
|
||||||
|
location: '/redirect/3.html',
|
||||||
|
Date: 'Fri, 19 Nov 2021 09:53:58 GMT',
|
||||||
|
Connection: 'keep-alive',
|
||||||
|
'Keep-Alive': 'timeout=5',
|
||||||
|
'Transfer-Encoding': 'chunked',
|
||||||
|
},
|
||||||
|
resourceIPAddressSpace: 'Local',
|
||||||
|
statusCode: 302,
|
||||||
|
headersText:
|
||||||
|
'HTTP/1.1 302 Found\r\nlocation: /redirect/3.html\r\nDate: Fri, 19 Nov 2021 09:53:58 GMT\r\nConnection: keep-alive\r\nKeep-Alive: timeout=5\r\nTransfer-Encoding: chunked\r\n\r\n',
|
||||||
|
});
|
||||||
|
mockCDPSession.emit('Network.requestWillBeSentExtraInfo', {
|
||||||
|
requestId: '7760711DEFCFA23132D98ABA6B4E175C',
|
||||||
|
associatedCookies: [],
|
||||||
|
headers: {
|
||||||
|
Host: 'localhost:8907',
|
||||||
|
Connection: 'keep-alive',
|
||||||
|
'Upgrade-Insecure-Requests': '1',
|
||||||
|
'User-Agent':
|
||||||
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/97.0.4691.0 Safari/537.36',
|
||||||
|
Accept:
|
||||||
|
'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
|
||||||
|
'Sec-Fetch-Site': 'none',
|
||||||
|
'Sec-Fetch-Mode': 'navigate',
|
||||||
|
'Sec-Fetch-User': '?1',
|
||||||
|
'Sec-Fetch-Dest': 'document',
|
||||||
|
'Accept-Encoding': 'gzip, deflate, br',
|
||||||
|
},
|
||||||
|
connectTiming: { requestTime: 2111.560482 },
|
||||||
|
});
|
||||||
|
mockCDPSession.emit('Network.requestWillBeSent', {
|
||||||
|
requestId: '7760711DEFCFA23132D98ABA6B4E175C',
|
||||||
|
loaderId: '7760711DEFCFA23132D98ABA6B4E175C',
|
||||||
|
documentURL: 'http://localhost:8907/empty.html',
|
||||||
|
request: {
|
||||||
|
url: 'http://localhost:8907/empty.html',
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Upgrade-Insecure-Requests': '1',
|
||||||
|
'User-Agent':
|
||||||
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/97.0.4691.0 Safari/537.36',
|
||||||
|
},
|
||||||
|
mixedContentType: 'none',
|
||||||
|
initialPriority: 'VeryHigh',
|
||||||
|
referrerPolicy: 'strict-origin-when-cross-origin',
|
||||||
|
isSameSite: true,
|
||||||
|
},
|
||||||
|
timestamp: 2111.561542,
|
||||||
|
wallTime: 1637315638.478837,
|
||||||
|
initiator: { type: 'other' },
|
||||||
|
redirectHasExtraInfo: true,
|
||||||
|
redirectResponse: {
|
||||||
|
url: 'http://localhost:8907/redirect/3.html',
|
||||||
|
status: 302,
|
||||||
|
statusText: 'Found',
|
||||||
|
headers: {
|
||||||
|
location: 'http://localhost:8907/empty.html',
|
||||||
|
Date: 'Fri, 19 Nov 2021 09:53:58 GMT',
|
||||||
|
Connection: 'keep-alive',
|
||||||
|
'Keep-Alive': 'timeout=5',
|
||||||
|
'Transfer-Encoding': 'chunked',
|
||||||
|
},
|
||||||
|
mimeType: '',
|
||||||
|
connectionReused: true,
|
||||||
|
connectionId: 322,
|
||||||
|
remoteIPAddress: '[::1]',
|
||||||
|
remotePort: 8907,
|
||||||
|
fromDiskCache: false,
|
||||||
|
fromServiceWorker: false,
|
||||||
|
fromPrefetchCache: false,
|
||||||
|
encodedDataLength: 178,
|
||||||
|
timing: {
|
||||||
|
requestTime: 2111.560482,
|
||||||
|
proxyStart: -1,
|
||||||
|
proxyEnd: -1,
|
||||||
|
dnsStart: -1,
|
||||||
|
dnsEnd: -1,
|
||||||
|
connectStart: -1,
|
||||||
|
connectEnd: -1,
|
||||||
|
sslStart: -1,
|
||||||
|
sslEnd: -1,
|
||||||
|
workerStart: -1,
|
||||||
|
workerReady: -1,
|
||||||
|
workerFetchStart: -1,
|
||||||
|
workerRespondWithSettled: -1,
|
||||||
|
sendStart: 0.149,
|
||||||
|
sendEnd: 0.198,
|
||||||
|
pushStart: 0,
|
||||||
|
pushEnd: 0,
|
||||||
|
receiveHeadersEnd: 0.478,
|
||||||
|
},
|
||||||
|
responseTime: 1.637315638478184e12,
|
||||||
|
protocol: 'http/1.1',
|
||||||
|
securityState: 'secure',
|
||||||
|
},
|
||||||
|
type: 'Document',
|
||||||
|
frameId: '099A5216AF03AAFEC988F214B024DF08',
|
||||||
|
hasUserGesture: false,
|
||||||
|
});
|
||||||
|
mockCDPSession.emit('Network.responseReceivedExtraInfo', {
|
||||||
|
requestId: '7760711DEFCFA23132D98ABA6B4E175C',
|
||||||
|
blockedCookies: [],
|
||||||
|
headers: {
|
||||||
|
location: 'http://localhost:8907/empty.html',
|
||||||
|
Date: 'Fri, 19 Nov 2021 09:53:58 GMT',
|
||||||
|
Connection: 'keep-alive',
|
||||||
|
'Keep-Alive': 'timeout=5',
|
||||||
|
'Transfer-Encoding': 'chunked',
|
||||||
|
},
|
||||||
|
resourceIPAddressSpace: 'Local',
|
||||||
|
statusCode: 302,
|
||||||
|
headersText:
|
||||||
|
'HTTP/1.1 302 Found\r\nlocation: http://localhost:8907/empty.html\r\nDate: Fri, 19 Nov 2021 09:53:58 GMT\r\nConnection: keep-alive\r\nKeep-Alive: timeout=5\r\nTransfer-Encoding: chunked\r\n\r\n',
|
||||||
|
});
|
||||||
|
mockCDPSession.emit('Network.requestWillBeSentExtraInfo', {
|
||||||
|
requestId: '7760711DEFCFA23132D98ABA6B4E175C',
|
||||||
|
associatedCookies: [],
|
||||||
|
headers: {
|
||||||
|
Host: 'localhost:8907',
|
||||||
|
Connection: 'keep-alive',
|
||||||
|
'Upgrade-Insecure-Requests': '1',
|
||||||
|
'User-Agent':
|
||||||
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/97.0.4691.0 Safari/537.36',
|
||||||
|
Accept:
|
||||||
|
'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
|
||||||
|
'Sec-Fetch-Site': 'none',
|
||||||
|
'Sec-Fetch-Mode': 'navigate',
|
||||||
|
'Sec-Fetch-User': '?1',
|
||||||
|
'Sec-Fetch-Dest': 'document',
|
||||||
|
'Accept-Encoding': 'gzip, deflate, br',
|
||||||
|
},
|
||||||
|
connectTiming: { requestTime: 2111.561759 },
|
||||||
|
});
|
||||||
|
mockCDPSession.emit('Network.responseReceivedExtraInfo', {
|
||||||
|
requestId: '7760711DEFCFA23132D98ABA6B4E175C',
|
||||||
|
blockedCookies: [],
|
||||||
|
headers: {
|
||||||
|
'Cache-Control': 'no-cache, no-store',
|
||||||
|
'Content-Type': 'text/html; charset=utf-8',
|
||||||
|
Date: 'Fri, 19 Nov 2021 09:53:58 GMT',
|
||||||
|
Connection: 'keep-alive',
|
||||||
|
'Keep-Alive': 'timeout=5',
|
||||||
|
'Content-Length': '0',
|
||||||
|
},
|
||||||
|
resourceIPAddressSpace: 'Local',
|
||||||
|
statusCode: 200,
|
||||||
|
headersText:
|
||||||
|
'HTTP/1.1 200 OK\r\nCache-Control: no-cache, no-store\r\nContent-Type: text/html; charset=utf-8\r\nDate: Fri, 19 Nov 2021 09:53:58 GMT\r\nConnection: keep-alive\r\nKeep-Alive: timeout=5\r\nContent-Length: 0\r\n\r\n',
|
||||||
|
});
|
||||||
|
mockCDPSession.emit('Network.responseReceived', {
|
||||||
|
requestId: '7760711DEFCFA23132D98ABA6B4E175C',
|
||||||
|
loaderId: '7760711DEFCFA23132D98ABA6B4E175C',
|
||||||
|
timestamp: 2111.563565,
|
||||||
|
type: 'Document',
|
||||||
|
response: {
|
||||||
|
url: 'http://localhost:8907/empty.html',
|
||||||
|
status: 200,
|
||||||
|
statusText: 'OK',
|
||||||
|
headers: {
|
||||||
|
'Cache-Control': 'no-cache, no-store',
|
||||||
|
'Content-Type': 'text/html; charset=utf-8',
|
||||||
|
Date: 'Fri, 19 Nov 2021 09:53:58 GMT',
|
||||||
|
Connection: 'keep-alive',
|
||||||
|
'Keep-Alive': 'timeout=5',
|
||||||
|
'Content-Length': '0',
|
||||||
|
},
|
||||||
|
mimeType: 'text/html',
|
||||||
|
connectionReused: true,
|
||||||
|
connectionId: 322,
|
||||||
|
remoteIPAddress: '[::1]',
|
||||||
|
remotePort: 8907,
|
||||||
|
fromDiskCache: false,
|
||||||
|
fromServiceWorker: false,
|
||||||
|
fromPrefetchCache: false,
|
||||||
|
encodedDataLength: 197,
|
||||||
|
timing: {
|
||||||
|
requestTime: 2111.561759,
|
||||||
|
proxyStart: -1,
|
||||||
|
proxyEnd: -1,
|
||||||
|
dnsStart: -1,
|
||||||
|
dnsEnd: -1,
|
||||||
|
connectStart: -1,
|
||||||
|
connectEnd: -1,
|
||||||
|
sslStart: -1,
|
||||||
|
sslEnd: -1,
|
||||||
|
workerStart: -1,
|
||||||
|
workerReady: -1,
|
||||||
|
workerFetchStart: -1,
|
||||||
|
workerRespondWithSettled: -1,
|
||||||
|
sendStart: 0.148,
|
||||||
|
sendEnd: 0.19,
|
||||||
|
pushStart: 0,
|
||||||
|
pushEnd: 0,
|
||||||
|
receiveHeadersEnd: 0.925,
|
||||||
|
},
|
||||||
|
responseTime: 1.637315638479928e12,
|
||||||
|
protocol: 'http/1.1',
|
||||||
|
securityState: 'secure',
|
||||||
|
},
|
||||||
|
hasExtraInfo: true,
|
||||||
|
frameId: '099A5216AF03AAFEC988F214B024DF08',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -168,7 +168,8 @@ describeFailsFirefox('Accessibility', function () {
|
|||||||
'<div tabIndex=-1 aria-roledescription="foo">Hi</div>'
|
'<div tabIndex=-1 aria-roledescription="foo">Hi</div>'
|
||||||
);
|
);
|
||||||
const snapshot = await page.accessibility.snapshot();
|
const snapshot = await page.accessibility.snapshot();
|
||||||
expect(snapshot.children[0].roledescription).toEqual('foo');
|
// See https://chromium-review.googlesource.com/c/chromium/src/+/3088862
|
||||||
|
expect(snapshot.children[0].roledescription).toEqual(undefined);
|
||||||
});
|
});
|
||||||
it('orientation', async () => {
|
it('orientation', async () => {
|
||||||
const { page } = getTestState();
|
const { page } = getTestState();
|
||||||
|
@ -21,6 +21,7 @@ import {
|
|||||||
setupTestBrowserHooks,
|
setupTestBrowserHooks,
|
||||||
setupTestPageAndContextHooks,
|
setupTestPageAndContextHooks,
|
||||||
itFailsFirefox,
|
itFailsFirefox,
|
||||||
|
shortWaitForArrayToHaveAtLeastNElements,
|
||||||
} from './mocha-utils'; // eslint-disable-line import/extensions
|
} from './mocha-utils'; // eslint-disable-line import/extensions
|
||||||
|
|
||||||
describe('JSHandle', function () {
|
describe('JSHandle', function () {
|
||||||
@ -390,6 +391,7 @@ describe('JSHandle', function () {
|
|||||||
y: 15,
|
y: 15,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
await shortWaitForArrayToHaveAtLeastNElements(clicks, 2);
|
||||||
expect(clicks).toEqual([
|
expect(clicks).toEqual([
|
||||||
[45 + 60, 45 + 30], // margin + middle point offset
|
[45 + 60, 45 + 30], // margin + middle point offset
|
||||||
[30 + 10, 30 + 15], // margin + offset
|
[30 + 10, 30 + 15], // margin + offset
|
||||||
|
@ -319,3 +319,17 @@ export const expectCookieEquals = (
|
|||||||
|
|
||||||
expect(cookies).toEqual(expectedCookies);
|
expect(cookies).toEqual(expectedCookies);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const shortWaitForArrayToHaveAtLeastNElements = async (
|
||||||
|
data: unknown[],
|
||||||
|
minLength: number,
|
||||||
|
attempts = 3,
|
||||||
|
timeout = 50
|
||||||
|
): Promise<void> => {
|
||||||
|
for (let i = 0; i < attempts; i++) {
|
||||||
|
if (data.length >= minLength) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, timeout));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -686,4 +686,77 @@ describe('network', function () {
|
|||||||
expect(responses.get('one-style.html').fromCache()).toBe(false);
|
expect(responses.get('one-style.html').fromCache()).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describeFailsFirefox('raw network headers', async () => {
|
||||||
|
it('Same-origin set-cookie navigation', async () => {
|
||||||
|
const { page, server } = getTestState();
|
||||||
|
|
||||||
|
const setCookieString = 'foo=bar';
|
||||||
|
server.setRoute('/empty.html', (req, res) => {
|
||||||
|
res.setHeader('set-cookie', setCookieString);
|
||||||
|
res.end('hello world');
|
||||||
|
});
|
||||||
|
const response = await page.goto(server.EMPTY_PAGE);
|
||||||
|
expect(response.headers()['set-cookie']).toBe(setCookieString);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Same-origin set-cookie subresource', async () => {
|
||||||
|
const { page, server } = getTestState();
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
|
||||||
|
const setCookieString = 'foo=bar';
|
||||||
|
server.setRoute('/foo', (req, res) => {
|
||||||
|
res.setHeader('set-cookie', setCookieString);
|
||||||
|
res.end('hello world');
|
||||||
|
});
|
||||||
|
|
||||||
|
const responsePromise = new Promise<HTTPResponse>((resolve) =>
|
||||||
|
page.on('response', (response) => resolve(response))
|
||||||
|
);
|
||||||
|
page.evaluate(() => {
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('GET', '/foo');
|
||||||
|
xhr.send();
|
||||||
|
});
|
||||||
|
const subresourceResponse = await responsePromise;
|
||||||
|
expect(subresourceResponse.headers()['set-cookie']).toBe(setCookieString);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Cross-origin set-cookie', async () => {
|
||||||
|
const { httpsServer, puppeteer, defaultBrowserOptions } = getTestState();
|
||||||
|
|
||||||
|
const browser = await puppeteer.launch({
|
||||||
|
...defaultBrowserOptions,
|
||||||
|
ignoreHTTPSErrors: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const page = await browser.newPage();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await page.goto(httpsServer.PREFIX + '/empty.html');
|
||||||
|
|
||||||
|
const setCookieString = 'hello=world';
|
||||||
|
httpsServer.setRoute('/setcookie.html', (req, res) => {
|
||||||
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||||
|
res.setHeader('set-cookie', setCookieString);
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
await page.goto(httpsServer.PREFIX + '/setcookie.html');
|
||||||
|
|
||||||
|
const response = await new Promise<HTTPResponse>((resolve) => {
|
||||||
|
page.on('response', resolve);
|
||||||
|
const url = httpsServer.CROSS_PROCESS_PREFIX + '/setcookie.html';
|
||||||
|
page.evaluate<(src: string) => void>((src) => {
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('GET', src);
|
||||||
|
xhr.send();
|
||||||
|
}, url);
|
||||||
|
});
|
||||||
|
expect(response.headers()['set-cookie']).toBe(setCookieString);
|
||||||
|
} finally {
|
||||||
|
await page.close();
|
||||||
|
await browser.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
const versionsPerRelease = new Map([
|
const versionsPerRelease = new Map([
|
||||||
// This is a mapping from Chromium version => Puppeteer version.
|
// This is a mapping from Chromium version => Puppeteer version.
|
||||||
// In Chromium roll patches, use 'NEXT' for the Puppeteer version.
|
// In Chromium roll patches, use 'NEXT' for the Puppeteer version.
|
||||||
|
['97.0.4692.0', 'NEXT'],
|
||||||
['93.0.4577.0', 'v10.2.0'],
|
['93.0.4577.0', 'v10.2.0'],
|
||||||
['92.0.4512.0', 'v10.0.0'],
|
['92.0.4512.0', 'v10.0.0'],
|
||||||
['91.0.4469.0', 'v9.0.0'],
|
['91.0.4469.0', 'v9.0.0'],
|
||||||
|
Loading…
Reference in New Issue
Block a user