| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import type * as Platform from '../../core/platform/platform.js'; |
| import * as SDK from '../../core/sdk/sdk.js'; |
| import type * as Protocol from '../../generated/protocol.js'; |
| |
| /* eslint-disable @typescript-eslint/no-explicit-any */ |
| |
| class HARBase { |
| readonly custom: Map<string, any>; |
| constructor(data: any) { |
| if (!data || typeof data !== 'object') { |
| throw new Error('First parameter is expected to be an object'); |
| } |
| this.custom = new Map(); |
| } |
| |
| static safeDate(data: any): Date { |
| const date = new Date(data); |
| if (!Number.isNaN(date.getTime())) { |
| return date; |
| } |
| throw new Error('Invalid date format'); |
| } |
| |
| static safeNumber(data: any): number { |
| const result = Number(data); |
| if (!Number.isNaN(result)) { |
| return result; |
| } |
| throw new Error('Casting to number results in NaN'); |
| } |
| |
| static optionalNumber(data: any): number|undefined { |
| return data !== undefined ? HARBase.safeNumber(data) : undefined; |
| } |
| |
| static optionalString(data: any): string|undefined { |
| return data !== undefined ? String(data) : undefined; |
| } |
| |
| customAsString(name: string): string|undefined { |
| const value = this.custom.get(name); |
| if (!value) { |
| return undefined; |
| } |
| return String(value); |
| } |
| |
| customAsNumber(name: string): number|undefined { |
| const value = this.custom.get(name); |
| if (!value) { |
| return undefined; |
| } |
| const numberValue = Number(value); |
| if (Number.isNaN(numberValue)) { |
| return undefined; |
| } |
| return numberValue; |
| } |
| |
| customAsArray(name: string): any[]|undefined { |
| const value = this.custom.get(name); |
| if (!value) { |
| return undefined; |
| } |
| return Array.isArray(value) ? value : undefined; |
| } |
| |
| customInitiator(): HARInitiator|undefined { |
| return this.custom.get('initiator'); |
| } |
| } |
| |
| export class HARRoot extends HARBase { |
| log: HARLog; |
| constructor(data: any) { |
| super(data); |
| this.log = new HARLog(data['log']); |
| } |
| } |
| |
| export class HARLog extends HARBase { |
| version: string; |
| creator: HARCreator; |
| browser: HARCreator|undefined; |
| pages: HARPage[]; |
| entries: HAREntry[]; |
| comment: string|undefined; |
| constructor(data: any) { |
| super(data); |
| this.version = String(data['version']); |
| this.creator = new HARCreator(data['creator']); |
| this.browser = data['browser'] ? new HARCreator(data['browser']) : undefined; |
| this.pages = Array.isArray(data['pages']) ? data['pages'].map(page => new HARPage(page)) : []; |
| if (!Array.isArray(data['entries'])) { |
| throw new Error('log.entries is expected to be an array'); |
| } |
| this.entries = data['entries'].map(entry => new HAREntry(entry)); |
| this.comment = HARBase.optionalString(data['comment']); |
| } |
| } |
| |
| class HARCreator extends HARBase { |
| name: string; |
| version: string; |
| comment: string|undefined; |
| constructor(data: any) { |
| super(data); |
| this.name = String(data['name']); |
| this.version = String(data['version']); |
| this.comment = HARBase.optionalString(data['comment']); |
| } |
| } |
| |
| export class HARPage extends HARBase { |
| startedDateTime: Date; |
| id: string; |
| title: string; |
| pageTimings: HARPageTimings; |
| comment: string|undefined; |
| constructor(data: any) { |
| super(data); |
| this.startedDateTime = HARBase.safeDate(data['startedDateTime']); |
| this.id = String(data['id']); |
| this.title = String(data['title']); |
| this.pageTimings = new HARPageTimings(data['pageTimings']); |
| this.comment = HARBase.optionalString(data['comment']); |
| } |
| } |
| |
| class HARPageTimings extends HARBase { |
| onContentLoad: number|undefined; |
| onLoad: number|undefined; |
| comment: string|undefined; |
| constructor(data: any) { |
| super(data); |
| this.onContentLoad = HARBase.optionalNumber(data['onContentLoad']); |
| this.onLoad = HARBase.optionalNumber(data['onLoad']); |
| this.comment = HARBase.optionalString(data['comment']); |
| } |
| } |
| |
| export class HAREntry extends HARBase { |
| pageref: string|undefined; |
| startedDateTime: Date; |
| time: number; |
| request: HARRequest; |
| response: HARResponse; |
| timings: HARTimings; |
| serverIPAddress: string|undefined; |
| connection: string|undefined; |
| comment: string|undefined; |
| constructor(data: any) { |
| super(data); |
| this.pageref = HARBase.optionalString(data['pageref']); |
| this.startedDateTime = HARBase.safeDate(data['startedDateTime']); |
| this.time = HARBase.safeNumber(data['time']); |
| this.request = new HARRequest(data['request']); |
| this.response = new HARResponse(data['response']); |
| this.timings = new HARTimings(data['timings']); |
| this.serverIPAddress = HARBase.optionalString(data['serverIPAddress']); |
| this.connection = HARBase.optionalString(data['connection']); |
| this.comment = HARBase.optionalString(data['comment']); |
| |
| // Chrome specific. |
| this.custom.set('connectionId', HARBase.optionalString(data['_connectionId'])); |
| this.custom.set('fromCache', HARBase.optionalString(data['_fromCache'])); |
| this.custom.set('initiator', this.importInitiator(data['_initiator'])); |
| this.custom.set('priority', HARBase.optionalString(data['_priority'])); |
| this.custom.set('resourceType', HARBase.optionalString(data['_resourceType'])); |
| this.custom.set('webSocketMessages', this.importWebSocketMessages(data['_webSocketMessages'])); |
| } |
| |
| private importInitiator(initiator: any): HARInitiator|undefined { |
| if (typeof initiator !== 'object') { |
| return; |
| } |
| |
| return new HARInitiator(initiator); |
| } |
| |
| private importWebSocketMessages(inputMessages: any): HARWebSocketMessage[]|undefined { |
| if (!Array.isArray(inputMessages)) { |
| return; |
| } |
| |
| const outputMessages = []; |
| for (const message of inputMessages) { |
| if (typeof message !== 'object') { |
| return; |
| } |
| outputMessages.push(new HARWebSocketMessage(message)); |
| } |
| return outputMessages; |
| } |
| } |
| |
| class HARRequest extends HARBase { |
| method: string; |
| url: Platform.DevToolsPath.UrlString; |
| httpVersion: string; |
| cookies: HARCookie[]; |
| headers: HARHeader[]; |
| queryString: HARQueryString[]; |
| postData: HARPostData|undefined; |
| headersSize: number; |
| bodySize: number; |
| comment: string|undefined; |
| constructor(data: any) { |
| super(data); |
| this.method = String(data['method']); |
| this.url = String(data['url']) as Platform.DevToolsPath.UrlString; |
| this.httpVersion = String(data['httpVersion']); |
| this.cookies = Array.isArray(data['cookies']) ? data['cookies'].map(cookie => new HARCookie(cookie)) : []; |
| this.headers = Array.isArray(data['headers']) ? data['headers'].map(header => new HARHeader(header)) : []; |
| this.queryString = Array.isArray(data['queryString']) ? data['queryString'].map(qs => new HARQueryString(qs)) : []; |
| this.postData = data['postData'] ? new HARPostData(data['postData']) : undefined; |
| this.headersSize = HARBase.safeNumber(data['headersSize']); |
| this.bodySize = HARBase.safeNumber(data['bodySize']); |
| this.comment = HARBase.optionalString(data['comment']); |
| } |
| } |
| |
| class HARResponse extends HARBase { |
| status: number; |
| statusText: string; |
| httpVersion: string; |
| cookies: HARCookie[]; |
| headers: HARHeader[]; |
| content: HARContent; |
| redirectURL: string; |
| headersSize: number; |
| bodySize: number; |
| comment: string|undefined; |
| constructor(data: any) { |
| super(data); |
| this.status = HARBase.safeNumber(data['status']); |
| this.statusText = String(data['statusText']); |
| this.httpVersion = String(data['httpVersion']); |
| this.cookies = Array.isArray(data['cookies']) ? data['cookies'].map(cookie => new HARCookie(cookie)) : []; |
| this.headers = Array.isArray(data['headers']) ? data['headers'].map(header => new HARHeader(header)) : []; |
| this.content = new HARContent(data['content']); |
| this.redirectURL = String(data['redirectURL']); |
| this.headersSize = HARBase.safeNumber(data['headersSize']); |
| this.bodySize = HARBase.safeNumber(data['bodySize']); |
| this.comment = HARBase.optionalString(data['comment']); |
| |
| // Chrome specific. |
| this.custom.set('transferSize', HARBase.optionalNumber(data['_transferSize'])); |
| this.custom.set('error', HARBase.optionalString(data['_error'])); |
| this.custom.set('fetchedViaServiceWorker', Boolean(data['_fetchedViaServiceWorker'])); |
| this.custom.set('responseCacheStorageCacheName', HARBase.optionalString(data['_responseCacheStorageCacheName'])); |
| this.custom.set('serviceWorkerResponseSource', HARBase.optionalString(data['_serviceWorkerResponseSource'])); |
| this.custom.set( |
| 'serviceWorkerRouterRuleIdMatched', HARBase.optionalNumber(data['_serviceWorkerRouterRuleIdMatched'])); |
| this.custom.set( |
| 'serviceWorkerRouterMatchedSourceType', HARBase.optionalString(data['_serviceWorkerRouterMatchedSourceType'])); |
| this.custom.set( |
| 'serviceWorkerRouterActualSourceType', HARBase.optionalString(data['_serviceWorkerRouterActualSourceType'])); |
| } |
| } |
| |
| class HARCookie extends HARBase { |
| name: string; |
| value: string; |
| path: string|undefined; |
| domain: string|undefined; |
| expires: Date|undefined; |
| httpOnly: boolean|undefined; |
| secure: boolean|undefined; |
| comment: string|undefined; |
| constructor(data: any) { |
| super(data); |
| this.name = String(data['name']); |
| this.value = String(data['value']); |
| this.path = HARBase.optionalString(data['path']); |
| this.domain = HARBase.optionalString(data['domain']); |
| this.expires = data['expires'] ? HARBase.safeDate(data['expires']) : undefined; |
| this.httpOnly = data['httpOnly'] !== undefined ? Boolean(data['httpOnly']) : undefined; |
| this.secure = data['secure'] !== undefined ? Boolean(data['secure']) : undefined; |
| this.comment = HARBase.optionalString(data['comment']); |
| } |
| } |
| |
| class HARHeader extends HARBase { |
| name: string; |
| value: string; |
| comment: string|undefined; |
| constructor(data: any) { |
| super(data); |
| this.name = String(data['name']); |
| this.value = String(data['value']); |
| this.comment = HARBase.optionalString(data['comment']); |
| } |
| } |
| |
| class HARQueryString extends HARBase { |
| name: string; |
| value: string; |
| comment: string|undefined; |
| constructor(data: any) { |
| super(data); |
| this.name = String(data['name']); |
| this.value = String(data['value']); |
| this.comment = HARBase.optionalString(data['comment']); |
| } |
| } |
| |
| class HARPostData extends HARBase { |
| mimeType: string; |
| params: HARParam[]; |
| text: string; |
| comment: string|undefined; |
| constructor(data: any) { |
| super(data); |
| this.mimeType = String(data['mimeType']); |
| this.params = Array.isArray(data['params']) ? data['params'].map(param => new HARParam(param)) : []; |
| this.text = String(data['text']); |
| this.comment = HARBase.optionalString(data['comment']); |
| } |
| } |
| |
| export class HARParam extends HARBase { |
| name: string; |
| value: string|undefined; |
| fileName: string|undefined; |
| contentType: string|undefined; |
| comment: string|undefined; |
| constructor(data: any) { |
| super(data); |
| this.name = String(data['name']); |
| this.value = HARBase.optionalString(data['value']); |
| this.fileName = HARBase.optionalString(data['fileName']); |
| this.contentType = HARBase.optionalString(data['contentType']); |
| this.comment = HARBase.optionalString(data['comment']); |
| } |
| } |
| |
| class HARContent extends HARBase { |
| size: number; |
| compression: number|undefined; |
| mimeType: string; |
| text: string|undefined; |
| encoding: string|undefined; |
| comment: string|undefined; |
| constructor(data: any) { |
| super(data); |
| this.size = HARBase.safeNumber(data['size']); |
| this.compression = HARBase.optionalNumber(data['compression']); |
| this.mimeType = String(data['mimeType']); |
| this.text = HARBase.optionalString(data['text']); |
| this.encoding = HARBase.optionalString(data['encoding']); |
| this.comment = HARBase.optionalString(data['comment']); |
| } |
| } |
| |
| export class HARTimings extends HARBase { |
| blocked: number|undefined; |
| dns: number|undefined; |
| connect: number|undefined; |
| send: number; |
| wait: number; |
| receive: number; |
| ssl: number|undefined; |
| comment: string|undefined; |
| constructor(data: any) { |
| super(data); |
| this.blocked = HARBase.optionalNumber(data['blocked']); |
| this.dns = HARBase.optionalNumber(data['dns']); |
| this.connect = HARBase.optionalNumber(data['connect']); |
| this.send = HARBase.safeNumber(data['send']); |
| this.wait = HARBase.safeNumber(data['wait']); |
| this.receive = HARBase.safeNumber(data['receive']); |
| this.ssl = HARBase.optionalNumber(data['ssl']); |
| this.comment = HARBase.optionalString(data['comment']); |
| |
| // Chrome specific. |
| this.custom.set('blocked_queueing', HARBase.optionalNumber(data['_blocked_queueing'])); |
| this.custom.set('blocked_proxy', HARBase.optionalNumber(data['_blocked_proxy'])); |
| |
| // Service Worker timing info (Chrome specific). |
| this.custom.set('workerStart', HARBase.optionalNumber(data['_workerStart'])); |
| this.custom.set('workerReady', HARBase.optionalNumber(data['_workerReady'])); |
| this.custom.set('workerFetchStart', HARBase.optionalNumber(data['_workerFetchStart'])); |
| this.custom.set('workerRespondWithSettled', HARBase.optionalNumber(data['_workerRespondWithSettled'])); |
| this.custom.set('workerRouterEvaluationStart', HARBase.optionalNumber(data['_workerRouterEvaluationStart'])); |
| this.custom.set('workerCacheLookupStart', HARBase.optionalNumber(data['_workerCacheLookupStart'])); |
| } |
| } |
| |
| export class HARInitiator extends HARBase { |
| type: Protocol.Network.InitiatorType; |
| url?: string; |
| lineNumber?: number; |
| requestId?: Protocol.Network.RequestId; |
| stack?: HARStack; |
| /** |
| * Based on Protocol.Network.Initiator defined in browser_protocol.pdl |
| */ |
| constructor(data: any) { |
| super(data); |
| this.type = (HARBase.optionalString(data['type']) ?? SDK.NetworkRequest.InitiatorType.OTHER) as |
| Protocol.Network.InitiatorType; |
| this.url = HARBase.optionalString(data['url']); |
| this.lineNumber = HARBase.optionalNumber(data['lineNumber']); |
| this.requestId = HARBase.optionalString(data['requestId']) as Protocol.Network.RequestId; |
| if (data['stack']) { |
| this.stack = new HARStack(data['stack']); |
| } |
| } |
| } |
| |
| export class HARStack extends HARBase { |
| description?: string; |
| callFrames: HARCallFrame[]; |
| parent?: HARStack; |
| parentId?: { |
| id: string, |
| debuggerId?: Protocol.Runtime.UniqueDebuggerId, |
| }; |
| /** |
| * Based on Protocol.Runtime.StackTrace defined in browser_protocol.pdl |
| */ |
| constructor(data: any) { |
| super(data); |
| |
| this.callFrames = Array.isArray(data.callFrames) ? |
| data.callFrames.map((item: any) => item ? new HARCallFrame(item) : null).filter(Boolean) : |
| []; |
| |
| if (data['parent']) { |
| this.parent = new HARStack(data['parent']); |
| } |
| |
| this.description = HARBase.optionalString(data['description']); |
| |
| const parentId = data['parentId']; |
| if (parentId) { |
| this.parentId = { |
| id: HARBase.optionalString(parentId['id']) ?? '', |
| debuggerId: HARBase.optionalString(parentId['debuggerId']) as Protocol.Runtime.UniqueDebuggerId | undefined, |
| }; |
| } |
| } |
| } |
| |
| export class HARCallFrame extends HARBase { |
| functionName: string; |
| scriptId: Protocol.Runtime.ScriptId; |
| url = ''; |
| lineNumber = -1; |
| columnNumber = -1; |
| /** |
| * Based on Protocol.Runtime.CallFrame defined in browser_protocol.pdl |
| */ |
| constructor(data: any) { |
| super(data); |
| |
| this.functionName = HARBase.optionalString(data['functionName']) ?? ''; |
| this.scriptId = (HARBase.optionalString(data['scriptId']) ?? '') as Protocol.Runtime.ScriptId; |
| this.url = HARBase.optionalString(data['url']) ?? ''; |
| this.lineNumber = HARBase.optionalNumber(data['lineNumber']) ?? -1; |
| this.columnNumber = HARBase.optionalNumber(data['columnNumber']) ?? -1; |
| } |
| } |
| |
| class HARWebSocketMessage extends HARBase { |
| time: number|undefined; |
| opcode: number|undefined; |
| data: string|undefined; |
| type: string|undefined; |
| constructor(data: any) { |
| super(data); |
| this.time = HARBase.optionalNumber(data['time']); |
| this.opcode = HARBase.optionalNumber(data['opcode']); |
| this.data = HARBase.optionalString(data['data']); |
| this.type = HARBase.optionalString(data['type']); |
| } |
| } |