chore: migrate src/NetworkManager to TypeScript (#5774)
This commit is contained in:
parent
862eea850e
commit
8654d630ad
@ -20,7 +20,7 @@ import {Events} from './Events';
|
||||
import {ExecutionContext, EVALUATION_SCRIPT_URL} from './ExecutionContext';
|
||||
import {LifecycleWatcher, PuppeteerLifeCycleEvent} from './LifecycleWatcher';
|
||||
import {DOMWorld, WaitForSelectorOptions} from './DOMWorld';
|
||||
import {NetworkManager} from './NetworkManager';
|
||||
import {NetworkManager, Response} from './NetworkManager';
|
||||
import {TimeoutSettings} from './TimeoutSettings';
|
||||
import {CDPSession} from './Connection';
|
||||
import {JSHandle, ElementHandle} from './JSHandle';
|
||||
@ -31,7 +31,7 @@ const UTILITY_WORLD_NAME = '__puppeteer_utility_world__';
|
||||
export class FrameManager extends EventEmitter {
|
||||
_client: CDPSession;
|
||||
_page: Puppeteer.Page;
|
||||
_networkManager: Puppeteer.NetworkManager;
|
||||
_networkManager: NetworkManager;
|
||||
_timeoutSettings: TimeoutSettings;
|
||||
_frames = new Map<string, Frame>();
|
||||
_contextIdToContext = new Map<number, ExecutionContext>();
|
||||
@ -70,11 +70,11 @@ export class FrameManager extends EventEmitter {
|
||||
]);
|
||||
}
|
||||
|
||||
networkManager(): Puppeteer.NetworkManager {
|
||||
networkManager(): NetworkManager {
|
||||
return this._networkManager;
|
||||
}
|
||||
|
||||
async navigateFrame(frame: Frame, url: string, options: {referer?: string; timeout?: number; waitUntil?: PuppeteerLifeCycleEvent|PuppeteerLifeCycleEvent[]} = {}): Promise<Puppeteer.Response | null> {
|
||||
async navigateFrame(frame: Frame, url: string, options: {referer?: string; timeout?: number; waitUntil?: PuppeteerLifeCycleEvent|PuppeteerLifeCycleEvent[]} = {}): Promise<Response | null> {
|
||||
assertNoLegacyNavigationOptions(options);
|
||||
const {
|
||||
referer = this._networkManager.extraHTTPHeaders()['referer'],
|
||||
@ -110,7 +110,7 @@ export class FrameManager extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
async waitForFrameNavigation(frame: Frame, options: {timeout?: number; waitUntil?: PuppeteerLifeCycleEvent|PuppeteerLifeCycleEvent[]} = {}): Promise<Puppeteer.Response | null> {
|
||||
async waitForFrameNavigation(frame: Frame, options: {timeout?: number; waitUntil?: PuppeteerLifeCycleEvent|PuppeteerLifeCycleEvent[]} = {}): Promise<Response | null> {
|
||||
assertNoLegacyNavigationOptions(options);
|
||||
const {
|
||||
waitUntil = ['load'],
|
||||
@ -333,11 +333,11 @@ export class Frame {
|
||||
this._parentFrame._childFrames.add(this);
|
||||
}
|
||||
|
||||
async goto(url: string, options: {referer?: string; timeout?: number; waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[]}): Promise<Puppeteer.Response | null> {
|
||||
async goto(url: string, options: {referer?: string; timeout?: number; waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[]}): Promise<Response | null> {
|
||||
return await this._frameManager.navigateFrame(this, url, options);
|
||||
}
|
||||
|
||||
async waitForNavigation(options: {timeout?: number; waitUntil?: PuppeteerLifeCycleEvent|PuppeteerLifeCycleEvent[]}): Promise<Puppeteer.Response | null> {
|
||||
async waitForNavigation(options: {timeout?: number; waitUntil?: PuppeteerLifeCycleEvent|PuppeteerLifeCycleEvent[]}): Promise<Response | null> {
|
||||
return await this._frameManager.waitForFrameNavigation(this, options);
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ import {helper, assert, PuppeteerEventListener} from './helper';
|
||||
import {Events} from './Events';
|
||||
import {TimeoutError} from './Errors';
|
||||
import {FrameManager, Frame} from './FrameManager';
|
||||
import {Request, Response} from './NetworkManager';
|
||||
|
||||
export type PuppeteerLifeCycleEvent = 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2';
|
||||
type ProtocolLifeCycleEvent = 'load' | 'DOMContentLoaded' | 'networkIdle' | 'networkAlmostIdle';
|
||||
@ -35,7 +36,7 @@ export class LifecycleWatcher {
|
||||
_frameManager: FrameManager;
|
||||
_frame: Frame;
|
||||
_timeout: number;
|
||||
_navigationRequest?: Puppeteer.Request;
|
||||
_navigationRequest?: Request;
|
||||
_eventListeners: PuppeteerEventListener[];
|
||||
_initialLoaderId: string;
|
||||
|
||||
@ -72,7 +73,6 @@ export class LifecycleWatcher {
|
||||
this._frame = frame;
|
||||
this._initialLoaderId = frame._loaderId;
|
||||
this._timeout = timeout;
|
||||
/** @type {?Puppeteer.Request} */
|
||||
this._navigationRequest = null;
|
||||
this._eventListeners = [
|
||||
helper.addEventListener(frameManager._client, Events.CDPSession.Disconnected, () => this._terminate(new Error('Navigation failed because browser has disconnected!'))),
|
||||
@ -101,7 +101,7 @@ export class LifecycleWatcher {
|
||||
this._checkLifecycleComplete();
|
||||
}
|
||||
|
||||
_onRequest(request: Puppeteer.Request): void {
|
||||
_onRequest(request: Request): void {
|
||||
if (request.frame() !== this._frame || !request.isNavigationRequest())
|
||||
return;
|
||||
this._navigationRequest = request;
|
||||
@ -115,7 +115,7 @@ export class LifecycleWatcher {
|
||||
this._checkLifecycleComplete();
|
||||
}
|
||||
|
||||
navigationResponse(): Puppeteer.Response | null {
|
||||
navigationResponse(): Response | null {
|
||||
return this._navigationRequest ? this._navigationRequest.response() : null;
|
||||
}
|
||||
|
||||
|
@ -13,44 +13,37 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
const EventEmitter = require('events');
|
||||
const {helper, assert, debugError} = require('./helper');
|
||||
const {Events} = require('./Events');
|
||||
// CDPSession is used only as a typedef
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const {CDPSession} = require('./Connection');
|
||||
// used only as a typedef
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const {Frame, FrameManager} = require('./FrameManager');
|
||||
import * as EventEmitter from 'events';
|
||||
import {helper, assert, debugError} from './helper';
|
||||
import {Events} from './Events';
|
||||
import {CDPSession} from './Connection';
|
||||
import {FrameManager, Frame} from './FrameManager';
|
||||
|
||||
class NetworkManager extends EventEmitter {
|
||||
/**
|
||||
* @param {!CDPSession} client
|
||||
* @param {!FrameManager} frameManager
|
||||
*/
|
||||
constructor(client, ignoreHTTPSErrors, frameManager) {
|
||||
interface Credentials {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export class NetworkManager extends EventEmitter {
|
||||
_client: CDPSession;
|
||||
_ignoreHTTPSErrors: boolean;
|
||||
_frameManager: FrameManager;
|
||||
_requestIdToRequest = new Map<string, Request>();
|
||||
_requestIdToRequestWillBeSentEvent = new Map<string, Protocol.Network.requestWillBeSentPayload>();
|
||||
_extraHTTPHeaders: Record<string, string> = {};
|
||||
_offline = false;
|
||||
_credentials?: Credentials = null;
|
||||
_attemptedAuthentications = new Set<string>();
|
||||
_userRequestInterceptionEnabled = false;
|
||||
_protocolRequestInterceptionEnabled = false;
|
||||
_userCacheDisabled = false;
|
||||
_requestIdToInterceptionId = new Map<string, string>();
|
||||
|
||||
constructor(client: CDPSession, ignoreHTTPSErrors: boolean, frameManager: FrameManager) {
|
||||
super();
|
||||
this._client = client;
|
||||
this._ignoreHTTPSErrors = ignoreHTTPSErrors;
|
||||
this._frameManager = frameManager;
|
||||
/** @type {!Map<string, !Request>} */
|
||||
this._requestIdToRequest = new Map();
|
||||
/** @type {!Map<string, !Protocol.Network.requestWillBeSentPayload>} */
|
||||
this._requestIdToRequestWillBeSentEvent = new Map();
|
||||
/** @type {!Object<string, string>} */
|
||||
this._extraHTTPHeaders = {};
|
||||
|
||||
this._offline = false;
|
||||
|
||||
/** @type {?{username: string, password: string}} */
|
||||
this._credentials = null;
|
||||
/** @type {!Set<string>} */
|
||||
this._attemptedAuthentications = new Set();
|
||||
this._userRequestInterceptionEnabled = false;
|
||||
this._protocolRequestInterceptionEnabled = false;
|
||||
this._userCacheDisabled = false;
|
||||
/** @type {!Map<string, string>} */
|
||||
this._requestIdToInterceptionId = new Map();
|
||||
|
||||
this._client.on('Fetch.requestPaused', this._onRequestPaused.bind(this));
|
||||
this._client.on('Fetch.authRequired', this._onAuthRequired.bind(this));
|
||||
@ -61,24 +54,18 @@ class NetworkManager extends EventEmitter {
|
||||
this._client.on('Network.loadingFailed', this._onLoadingFailed.bind(this));
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
async initialize(): Promise<void> {
|
||||
await this._client.send('Network.enable');
|
||||
if (this._ignoreHTTPSErrors)
|
||||
await this._client.send('Security.setIgnoreCertificateErrors', {ignore: true});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {?{username: string, password: string}} credentials
|
||||
*/
|
||||
async authenticate(credentials) {
|
||||
async authenticate(credentials?: Credentials): Promise<void> {
|
||||
this._credentials = credentials;
|
||||
await this._updateProtocolRequestInterception();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Object<string, string>} extraHTTPHeaders
|
||||
*/
|
||||
async setExtraHTTPHeaders(extraHTTPHeaders) {
|
||||
async setExtraHTTPHeaders(extraHTTPHeaders: Record<string, string>): Promise<void> {
|
||||
this._extraHTTPHeaders = {};
|
||||
for (const key of Object.keys(extraHTTPHeaders)) {
|
||||
const value = extraHTTPHeaders[key];
|
||||
@ -88,17 +75,11 @@ class NetworkManager extends EventEmitter {
|
||||
await this._client.send('Network.setExtraHTTPHeaders', {headers: this._extraHTTPHeaders});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Object<string, string>}
|
||||
*/
|
||||
extraHTTPHeaders() {
|
||||
extraHTTPHeaders(): Record<string, string> {
|
||||
return Object.assign({}, this._extraHTTPHeaders);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean} value
|
||||
*/
|
||||
async setOfflineMode(value) {
|
||||
async setOfflineMode(value: boolean): Promise<void> {
|
||||
if (this._offline === value)
|
||||
return;
|
||||
this._offline = value;
|
||||
@ -111,30 +92,21 @@ class NetworkManager extends EventEmitter {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} userAgent
|
||||
*/
|
||||
async setUserAgent(userAgent) {
|
||||
async setUserAgent(userAgent: string): Promise<void> {
|
||||
await this._client.send('Network.setUserAgentOverride', {userAgent});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean} enabled
|
||||
*/
|
||||
async setCacheEnabled(enabled) {
|
||||
async setCacheEnabled(enabled: boolean): Promise<void> {
|
||||
this._userCacheDisabled = !enabled;
|
||||
await this._updateProtocolCacheDisabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean} value
|
||||
*/
|
||||
async setRequestInterception(value) {
|
||||
async setRequestInterception(value: boolean): Promise<void> {
|
||||
this._userRequestInterceptionEnabled = value;
|
||||
await this._updateProtocolRequestInterception();
|
||||
}
|
||||
|
||||
async _updateProtocolRequestInterception() {
|
||||
async _updateProtocolRequestInterception(): Promise<void> {
|
||||
const enabled = this._userRequestInterceptionEnabled || !!this._credentials;
|
||||
if (enabled === this._protocolRequestInterceptionEnabled)
|
||||
return;
|
||||
@ -155,16 +127,13 @@ class NetworkManager extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
async _updateProtocolCacheDisabled() {
|
||||
async _updateProtocolCacheDisabled(): Promise<void> {
|
||||
await this._client.send('Network.setCacheDisabled', {
|
||||
cacheDisabled: this._userCacheDisabled || this._protocolRequestInterceptionEnabled
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Protocol.Network.requestWillBeSentPayload} event
|
||||
*/
|
||||
_onRequestWillBeSent(event) {
|
||||
_onRequestWillBeSent(event: Protocol.Network.requestWillBeSentPayload): void {
|
||||
// Request interception doesn't happen for data URLs with Network Service.
|
||||
if (this._protocolRequestInterceptionEnabled && !event.request.url.startsWith('data:')) {
|
||||
const requestId = event.requestId;
|
||||
@ -183,9 +152,12 @@ class NetworkManager extends EventEmitter {
|
||||
/**
|
||||
* @param {!Protocol.Fetch.authRequiredPayload} event
|
||||
*/
|
||||
_onAuthRequired(event) {
|
||||
/** @type {"Default"|"CancelAuth"|"ProvideCredentials"} */
|
||||
let response = 'Default';
|
||||
_onAuthRequired(event: Protocol.Fetch.authRequiredPayload): void {
|
||||
/* TODO(jacktfranklin): This is defined in protocol.d.ts but not
|
||||
* in an easily referrable way - we should look at exposing it.
|
||||
*/
|
||||
type AuthResponse = 'Default'|'CancelAuth'|'ProvideCredentials';
|
||||
let response: AuthResponse = 'Default';
|
||||
if (this._attemptedAuthentications.has(event.requestId)) {
|
||||
response = 'CancelAuth';
|
||||
} else if (this._credentials) {
|
||||
@ -199,10 +171,7 @@ class NetworkManager extends EventEmitter {
|
||||
}).catch(debugError);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Protocol.Fetch.requestPausedPayload} event
|
||||
*/
|
||||
_onRequestPaused(event) {
|
||||
_onRequestPaused(event: Protocol.Fetch.requestPausedPayload): void {
|
||||
if (!this._userRequestInterceptionEnabled && this._protocolRequestInterceptionEnabled) {
|
||||
this._client.send('Fetch.continueRequest', {
|
||||
requestId: event.requestId
|
||||
@ -220,11 +189,7 @@ class NetworkManager extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Protocol.Network.requestWillBeSentPayload} event
|
||||
* @param {?string} interceptionId
|
||||
*/
|
||||
_onRequest(event, interceptionId) {
|
||||
_onRequest(event: Protocol.Network.requestWillBeSentPayload, interceptionId?: string): void {
|
||||
let redirectChain = [];
|
||||
if (event.redirectResponse) {
|
||||
const request = this._requestIdToRequest.get(event.requestId);
|
||||
@ -240,21 +205,13 @@ class NetworkManager extends EventEmitter {
|
||||
this.emit(Events.NetworkManager.Request, request);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {!Protocol.Network.requestServedFromCachePayload} event
|
||||
*/
|
||||
_onRequestServedFromCache(event) {
|
||||
_onRequestServedFromCache(event: Protocol.Network.requestServedFromCachePayload): void {
|
||||
const request = this._requestIdToRequest.get(event.requestId);
|
||||
if (request)
|
||||
request._fromMemoryCache = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Request} request
|
||||
* @param {!Protocol.Network.Response} responsePayload
|
||||
*/
|
||||
_handleRequestRedirect(request, responsePayload) {
|
||||
_handleRequestRedirect(request: Request, responsePayload: Protocol.Network.Response): void {
|
||||
const response = new Response(this._client, request, responsePayload);
|
||||
request._response = response;
|
||||
request._redirectChain.push(request);
|
||||
@ -265,10 +222,7 @@ class NetworkManager extends EventEmitter {
|
||||
this.emit(Events.NetworkManager.RequestFinished, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Protocol.Network.responseReceivedPayload} event
|
||||
*/
|
||||
_onResponseReceived(event) {
|
||||
_onResponseReceived(event: Protocol.Network.responseReceivedPayload): void {
|
||||
const request = this._requestIdToRequest.get(event.requestId);
|
||||
// FileUpload sends a response without a matching request.
|
||||
if (!request)
|
||||
@ -278,10 +232,7 @@ class NetworkManager extends EventEmitter {
|
||||
this.emit(Events.NetworkManager.Response, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Protocol.Network.loadingFinishedPayload} event
|
||||
*/
|
||||
_onLoadingFinished(event) {
|
||||
_onLoadingFinished(event: Protocol.Network.loadingFinishedPayload): void {
|
||||
const request = this._requestIdToRequest.get(event.requestId);
|
||||
// For certain requestIds we never receive requestWillBeSent event.
|
||||
// @see https://crbug.com/750469
|
||||
@ -297,10 +248,7 @@ class NetworkManager extends EventEmitter {
|
||||
this.emit(Events.NetworkManager.RequestFinished, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Protocol.Network.loadingFailedPayload} event
|
||||
*/
|
||||
_onLoadingFailed(event) {
|
||||
_onLoadingFailed(event: Protocol.Network.loadingFailedPayload): void {
|
||||
const request = this._requestIdToRequest.get(event.requestId);
|
||||
// For certain requestIds we never receive requestWillBeSent event.
|
||||
// @see https://crbug.com/750469
|
||||
@ -316,105 +264,83 @@ class NetworkManager extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
class Request {
|
||||
/**
|
||||
* @param {!CDPSession} client
|
||||
* @param {?Frame} frame
|
||||
* @param {string} interceptionId
|
||||
* @param {boolean} allowInterception
|
||||
* @param {!Protocol.Network.requestWillBeSentPayload} event
|
||||
* @param {!Array<!Request>} redirectChain
|
||||
*/
|
||||
constructor(client, frame, interceptionId, allowInterception, event, redirectChain) {
|
||||
export class Request {
|
||||
_client: CDPSession;
|
||||
_requestId: string;
|
||||
_isNavigationRequest: boolean;
|
||||
_interceptionId: string;
|
||||
_allowInterception: boolean;
|
||||
_interceptionHandled = false;
|
||||
_response: Response | null = null;
|
||||
_failureText = null;
|
||||
_url: string;
|
||||
_resourceType: string;
|
||||
|
||||
_method: string;
|
||||
_postData?: string;
|
||||
_headers: Record<string, string> = {};
|
||||
_frame: Frame;
|
||||
|
||||
_redirectChain: Request[];
|
||||
_fromMemoryCache = false;
|
||||
|
||||
constructor(client: CDPSession, frame: Frame, interceptionId: string, allowInterception: boolean, event: Protocol.Network.requestWillBeSentPayload, redirectChain: Request[]) {
|
||||
this._client = client;
|
||||
this._requestId = event.requestId;
|
||||
this._isNavigationRequest = event.requestId === event.loaderId && event.type === 'Document';
|
||||
this._interceptionId = interceptionId;
|
||||
this._allowInterception = allowInterception;
|
||||
this._interceptionHandled = false;
|
||||
this._response = null;
|
||||
this._failureText = null;
|
||||
|
||||
this._url = event.request.url;
|
||||
this._resourceType = event.type.toLowerCase();
|
||||
this._method = event.request.method;
|
||||
this._postData = event.request.postData;
|
||||
this._headers = {};
|
||||
this._frame = frame;
|
||||
this._redirectChain = redirectChain;
|
||||
|
||||
for (const key of Object.keys(event.request.headers))
|
||||
this._headers[key.toLowerCase()] = event.request.headers[key];
|
||||
|
||||
this._fromMemoryCache = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
url() {
|
||||
url(): string {
|
||||
return this._url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
resourceType() {
|
||||
resourceType(): string {
|
||||
return this._resourceType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
method() {
|
||||
method(): string {
|
||||
return this._method;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {string|undefined}
|
||||
*/
|
||||
postData() {
|
||||
postData(): string | undefined {
|
||||
return this._postData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Object}
|
||||
*/
|
||||
headers() {
|
||||
headers(): Record<string, string> {
|
||||
return this._headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {?Response}
|
||||
*/
|
||||
response() {
|
||||
response(): Response | null {
|
||||
return this._response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {?Frame}
|
||||
*/
|
||||
frame() {
|
||||
frame(): Frame | null {
|
||||
return this._frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {boolean}
|
||||
*/
|
||||
isNavigationRequest() {
|
||||
isNavigationRequest(): boolean {
|
||||
return this._isNavigationRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Array<!Request>}
|
||||
*/
|
||||
redirectChain() {
|
||||
redirectChain(): Request[] {
|
||||
return this._redirectChain.slice();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {?{errorText: string}}
|
||||
*/
|
||||
failure() {
|
||||
failure(): {errorText: string} | null {
|
||||
if (!this._failureText)
|
||||
return null;
|
||||
return {
|
||||
@ -422,10 +348,7 @@ class Request {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!{url?: string, method?:string, postData?: string, headers?: !Object}} overrides
|
||||
*/
|
||||
async continue(overrides = {}) {
|
||||
async continue(overrides: {url?: string; method?: string; postData?: string; headers?: Record<string, string>} = {}): Promise<void> {
|
||||
// Request interception is not supported for data: urls.
|
||||
if (this._url.startsWith('data:'))
|
||||
return;
|
||||
@ -451,10 +374,12 @@ class Request {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!{status: number, headers: Object, contentType: string, body: (string|Buffer)}} response
|
||||
*/
|
||||
async respond(response) {
|
||||
async respond(response: {
|
||||
status: number;
|
||||
headers: Record<string, string>;
|
||||
contentType: string;
|
||||
body: string|Buffer;
|
||||
}): Promise<void> {
|
||||
// Mocking responses for dataURL requests is not currently supported.
|
||||
if (this._url.startsWith('data:'))
|
||||
return;
|
||||
@ -462,10 +387,9 @@ class Request {
|
||||
assert(!this._interceptionHandled, 'Request is already handled!');
|
||||
this._interceptionHandled = true;
|
||||
|
||||
const responseBody = response.body && helper.isString(response.body) ? Buffer.from(/** @type {string} */(response.body)) : /** @type {?Buffer} */(response.body || null);
|
||||
const responseBody: Buffer | null = response.body && helper.isString(response.body) ? Buffer.from(response.body) : response.body as Buffer || null;
|
||||
|
||||
/** @type {!Object<string, string>} */
|
||||
const responseHeaders = {};
|
||||
const responseHeaders: Record<string, string> = {};
|
||||
if (response.headers) {
|
||||
for (const header of Object.keys(response.headers))
|
||||
responseHeaders[header.toLowerCase()] = response.headers[header];
|
||||
@ -488,10 +412,7 @@ class Request {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string=} errorCode
|
||||
*/
|
||||
async abort(errorCode = 'failed') {
|
||||
async abort(errorCode: ErrorCode = 'failed'): Promise<void> {
|
||||
// Request interception is not supported for data: urls.
|
||||
if (this._url.startsWith('data:'))
|
||||
return;
|
||||
@ -511,7 +432,9 @@ class Request {
|
||||
}
|
||||
}
|
||||
|
||||
const errorReasons = {
|
||||
type ErrorCode = 'aborted' | 'accessdenied' | 'addressunreachable' | 'blockedbyclient' | 'blockedbyresponse' | 'connectionaborted' | 'connectionclosed' | 'connectionfailed' | 'connectionrefused' | 'connectionreset' | 'internetdisconnected' | 'namenotresolved' | 'timedout' | 'failed';
|
||||
|
||||
const errorReasons: Record<ErrorCode, Protocol.Network.ErrorReason> = {
|
||||
'aborted': 'Aborted',
|
||||
'accessdenied': 'AccessDenied',
|
||||
'addressunreachable': 'AddressUnreachable',
|
||||
@ -526,18 +449,31 @@ const errorReasons = {
|
||||
'namenotresolved': 'NameNotResolved',
|
||||
'timedout': 'TimedOut',
|
||||
'failed': 'Failed',
|
||||
};
|
||||
} as const;
|
||||
|
||||
class Response {
|
||||
/**
|
||||
* @param {!CDPSession} client
|
||||
* @param {!Request} request
|
||||
* @param {!Protocol.Network.Response} responsePayload
|
||||
*/
|
||||
constructor(client, request, responsePayload) {
|
||||
interface RemoteAddress {
|
||||
ip: string;
|
||||
port: number;
|
||||
}
|
||||
|
||||
export class Response {
|
||||
_client: CDPSession;
|
||||
_request: Request;
|
||||
_contentPromise: Promise<Buffer> | null = null;
|
||||
_bodyLoadedPromise: Promise<boolean>;
|
||||
_bodyLoadedPromiseFulfill: (x: boolean) => void;
|
||||
_remoteAddress: RemoteAddress;
|
||||
_status: number;
|
||||
_statusText: string;
|
||||
_url: string;
|
||||
_fromDiskCache: boolean;
|
||||
_fromServiceWorker: boolean;
|
||||
_headers: Record<string, string> = {};
|
||||
_securityDetails: SecurityDetails | null;
|
||||
|
||||
constructor(client: CDPSession, request: Request, responsePayload: Protocol.Network.Response) {
|
||||
this._client = client;
|
||||
this._request = request;
|
||||
this._contentPromise = null;
|
||||
|
||||
this._bodyLoadedPromise = new Promise(fulfill => {
|
||||
this._bodyLoadedPromiseFulfill = fulfill;
|
||||
@ -552,65 +488,40 @@ class Response {
|
||||
this._url = request.url();
|
||||
this._fromDiskCache = !!responsePayload.fromDiskCache;
|
||||
this._fromServiceWorker = !!responsePayload.fromServiceWorker;
|
||||
this._headers = {};
|
||||
for (const key of Object.keys(responsePayload.headers))
|
||||
this._headers[key.toLowerCase()] = responsePayload.headers[key];
|
||||
this._securityDetails = responsePayload.securityDetails ? new SecurityDetails(responsePayload.securityDetails) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {{ip: string, port: number}}
|
||||
*/
|
||||
remoteAddress() {
|
||||
remoteAddress(): RemoteAddress {
|
||||
return this._remoteAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
url() {
|
||||
url(): string {
|
||||
return this._url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {boolean}
|
||||
*/
|
||||
ok() {
|
||||
ok(): boolean {
|
||||
return this._status === 0 || (this._status >= 200 && this._status <= 299);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {number}
|
||||
*/
|
||||
status() {
|
||||
status(): number {
|
||||
return this._status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
statusText() {
|
||||
statusText(): string {
|
||||
return this._statusText;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Object}
|
||||
*/
|
||||
headers() {
|
||||
headers(): Record<string, string> {
|
||||
return this._headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {?SecurityDetails}
|
||||
*/
|
||||
securityDetails() {
|
||||
securityDetails(): SecurityDetails | null {
|
||||
return this._securityDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Promise<!Buffer>}
|
||||
*/
|
||||
buffer() {
|
||||
buffer(): Promise<Buffer> {
|
||||
if (!this._contentPromise) {
|
||||
this._contentPromise = this._bodyLoadedPromise.then(async error => {
|
||||
if (error)
|
||||
@ -624,104 +535,70 @@ class Response {
|
||||
return this._contentPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Promise<string>}
|
||||
*/
|
||||
async text() {
|
||||
async text(): Promise<string> {
|
||||
const content = await this.buffer();
|
||||
return content.toString('utf8');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Promise<!Object>}
|
||||
*/
|
||||
async json() {
|
||||
async json(): Promise<any> {
|
||||
const content = await this.text();
|
||||
return JSON.parse(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Request}
|
||||
*/
|
||||
request() {
|
||||
request(): Request {
|
||||
return this._request;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {boolean}
|
||||
*/
|
||||
fromCache() {
|
||||
fromCache(): boolean {
|
||||
return this._fromDiskCache || this._request._fromMemoryCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {boolean}
|
||||
*/
|
||||
fromServiceWorker() {
|
||||
fromServiceWorker(): boolean {
|
||||
return this._fromServiceWorker;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {?Frame}
|
||||
*/
|
||||
frame() {
|
||||
frame(): Frame | null {
|
||||
return this._request.frame();
|
||||
}
|
||||
}
|
||||
|
||||
class SecurityDetails {
|
||||
/**
|
||||
* @param {!Protocol.Network.SecurityDetails} securityPayload
|
||||
*/
|
||||
constructor(securityPayload) {
|
||||
this._subjectName = securityPayload['subjectName'];
|
||||
this._issuer = securityPayload['issuer'];
|
||||
this._validFrom = securityPayload['validFrom'];
|
||||
this._validTo = securityPayload['validTo'];
|
||||
this._protocol = securityPayload['protocol'];
|
||||
export class SecurityDetails {
|
||||
_subjectName: string;
|
||||
_issuer: string;
|
||||
_validFrom: number;
|
||||
_validTo: number;
|
||||
_protocol: string;
|
||||
|
||||
constructor(securityPayload: Protocol.Network.SecurityDetails) {
|
||||
this._subjectName = securityPayload.subjectName;
|
||||
this._issuer = securityPayload.issuer;
|
||||
this._validFrom = securityPayload.validFrom;
|
||||
this._validTo = securityPayload.validTo;
|
||||
this._protocol = securityPayload.protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
subjectName() {
|
||||
subjectName(): string {
|
||||
return this._subjectName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
issuer() {
|
||||
issuer(): string {
|
||||
return this._issuer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {number}
|
||||
*/
|
||||
validFrom() {
|
||||
validFrom(): number {
|
||||
return this._validFrom;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {number}
|
||||
*/
|
||||
validTo() {
|
||||
validTo(): number {
|
||||
return this._validTo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
protocol() {
|
||||
protocol(): string {
|
||||
return this._protocol;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object<string, string>} headers
|
||||
* @return {!Array<{name: string, value: string}>}
|
||||
*/
|
||||
function headersArray(headers) {
|
||||
function headersArray(headers: Record<string, string>): Array<{name: string; value: string}> {
|
||||
const result = [];
|
||||
for (const name in headers) {
|
||||
if (!Object.is(headers[name], undefined))
|
||||
@ -795,6 +672,4 @@ const STATUS_TEXTS = {
|
||||
'508': 'Loop Detected',
|
||||
'510': 'Not Extended',
|
||||
'511': 'Network Authentication Required',
|
||||
};
|
||||
|
||||
module.exports = {Request, Response, NetworkManager, SecurityDetails};
|
||||
} as const;
|
23
src/Page.js
23
src/Page.js
@ -40,6 +40,9 @@ const {Target} = require('./Target');
|
||||
// Import used as typedef
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const {createJSHandle, JSHandle, ElementHandle} = require('./JSHandle');
|
||||
// Import used as typedef
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const {Request: PuppeteerRequest, Response: PuppeteerResponse} = require('./NetworkManager');
|
||||
const {Accessibility} = require('./Accessibility');
|
||||
const {TimeoutSettings} = require('./TimeoutSettings');
|
||||
|
||||
@ -692,7 +695,7 @@ class Page extends EventEmitter {
|
||||
/**
|
||||
* @param {string} url
|
||||
* @param {!{referer?: string, timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array<!Puppeteer.PuppeteerLifeCycleEvent>}=} options
|
||||
* @return {!Promise<?Puppeteer.Response>}
|
||||
* @return {!Promise<?PuppeteerResponse>}
|
||||
*/
|
||||
async goto(url, options) {
|
||||
return await this._frameManager.mainFrame().goto(url, options);
|
||||
@ -700,7 +703,7 @@ class Page extends EventEmitter {
|
||||
|
||||
/**
|
||||
* @param {!{timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array<!Puppeteer.PuppeteerLifeCycleEvent>}=} options
|
||||
* @return {!Promise<?Puppeteer.Response>}
|
||||
* @return {!Promise<?PuppeteerResponse>}
|
||||
*/
|
||||
async reload(options) {
|
||||
const result = await Promise.all([
|
||||
@ -708,13 +711,13 @@ class Page extends EventEmitter {
|
||||
this._client.send('Page.reload')
|
||||
]);
|
||||
|
||||
const response = /** @type Puppeteer.Response */ (result[0]);
|
||||
const response = /** @type PuppeteerResponse */ (result[0]);
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!{timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array<!Puppeteer.PuppeteerLifeCycleEvent>}=} options
|
||||
* @return {!Promise<?Puppeteer.Response>}
|
||||
* @return {!Promise<?PuppeteerResponse>}
|
||||
*/
|
||||
async waitForNavigation(options = {}) {
|
||||
return await this._frameManager.mainFrame().waitForNavigation(options);
|
||||
@ -729,7 +732,7 @@ class Page extends EventEmitter {
|
||||
/**
|
||||
* @param {(string|Function)} urlOrPredicate
|
||||
* @param {!{timeout?: number}=} options
|
||||
* @return {!Promise<!Puppeteer.Request>}
|
||||
* @return {!Promise<!PuppeteerRequest>}
|
||||
*/
|
||||
async waitForRequest(urlOrPredicate, options = {}) {
|
||||
const {
|
||||
@ -747,7 +750,7 @@ class Page extends EventEmitter {
|
||||
/**
|
||||
* @param {(string|Function)} urlOrPredicate
|
||||
* @param {!{timeout?: number}=} options
|
||||
* @return {!Promise<!Puppeteer.Response>}
|
||||
* @return {!Promise<!PuppeteerResponse>}
|
||||
*/
|
||||
async waitForResponse(urlOrPredicate, options = {}) {
|
||||
const {
|
||||
@ -764,7 +767,7 @@ class Page extends EventEmitter {
|
||||
|
||||
/**
|
||||
* @param {!{timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array<!Puppeteer.PuppeteerLifeCycleEvent>}=} options
|
||||
* @return {!Promise<?Puppeteer.Response>}
|
||||
* @return {!Promise<?PuppeteerResponse>}
|
||||
*/
|
||||
async goBack(options) {
|
||||
return this._go(-1, options);
|
||||
@ -772,7 +775,7 @@ class Page extends EventEmitter {
|
||||
|
||||
/**
|
||||
* @param {!{timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array<!Puppeteer.PuppeteerLifeCycleEvent>}=} options
|
||||
* @return {!Promise<?Puppeteer.Response>}
|
||||
* @return {!Promise<?PuppeteerResponse>}
|
||||
*/
|
||||
async goForward(options) {
|
||||
return this._go(+1, options);
|
||||
@ -780,7 +783,7 @@ class Page extends EventEmitter {
|
||||
|
||||
/**
|
||||
* @param {!{timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array<!Puppeteer.PuppeteerLifeCycleEvent>}=} options
|
||||
* @return {!Promise<?Puppeteer.Response>}
|
||||
* @return {!Promise<?PuppeteerResponse>}
|
||||
*/
|
||||
async _go(delta, options) {
|
||||
const history = await this._client.send('Page.getNavigationHistory');
|
||||
@ -791,7 +794,7 @@ class Page extends EventEmitter {
|
||||
this.waitForNavigation(options),
|
||||
this._client.send('Page.navigateToHistoryEntry', {entryId: entry.id}),
|
||||
]);
|
||||
const response = /** @type Puppeteer.Response */ (result[0]);
|
||||
const response = /** @type PuppeteerResponse */ (result[0]);
|
||||
return response;
|
||||
}
|
||||
|
||||
|
4
src/externs.d.ts
vendored
4
src/externs.d.ts
vendored
@ -1,12 +1,8 @@
|
||||
import {Page as RealPage} from './Page.js';
|
||||
import { NetworkManager as RealNetworkManager, Request as RealRequest, Response as RealResponse } from './NetworkManager.js';
|
||||
import * as child_process from 'child_process';
|
||||
declare global {
|
||||
module Puppeteer {
|
||||
export class NetworkManager extends RealNetworkManager {}
|
||||
export class Page extends RealPage { }
|
||||
export class Response extends RealResponse { }
|
||||
export class Request extends RealRequest { }
|
||||
|
||||
|
||||
/* TODO(jacktfranklin@): once DOMWorld, Page, and FrameManager are in TS
|
||||
|
@ -307,6 +307,10 @@ function compareDocumentations(actual, expected) {
|
||||
actualName: 'Object',
|
||||
expectedName: 'WaitForSelectorOptions'
|
||||
}],
|
||||
['Method Request.abort() errorCode', {
|
||||
actualName: 'string',
|
||||
expectedName: 'ErrorCode'
|
||||
}],
|
||||
['Method Frame.goto() options.waitUntil', {
|
||||
actualName: '"load"|"domcontentloaded"|"networkidle0"|"networkidle2"|Array',
|
||||
expectedName: '"load"|"domcontentloaded"|"networkidle0"|"networkidle2"|Array<PuppeteerLifeCycleEvent>'
|
||||
|
Loading…
Reference in New Issue
Block a user