diff --git a/packages/playwright-core/src/client/harRouter.ts b/packages/playwright-core/src/client/harRouter.ts index ce15a7e222..b7c099ddb2 100644 --- a/packages/playwright-core/src/client/harRouter.ts +++ b/packages/playwright-core/src/client/harRouter.ts @@ -30,8 +30,10 @@ export class HarRouter { private _harId: string; static async create(localUtils: LocalUtils, options: HarOptions): Promise { - const { harId } = await localUtils._channel.harOpen({ file: options.path }); - return new HarRouter(localUtils, harId, options); + const { harId, error } = await localUtils._channel.harOpen({ file: options.path }); + if (error) + throw new Error(error); + return new HarRouter(localUtils, harId!, options); } constructor(localUtils: LocalUtils, harId: string, options?: HarOptions) { @@ -65,7 +67,7 @@ export class HarRouter { } if (response.action === 'error') - debugLogger.log('api', response.message!); + debugLogger.log('api', 'HAR: ' + response.message!); // Report the error, but fall through to the default handler. if (this._options?.fallback === 'continue') { diff --git a/packages/playwright-core/src/protocol/channels.ts b/packages/playwright-core/src/protocol/channels.ts index dfe5583de2..95c103af07 100644 --- a/packages/playwright-core/src/protocol/channels.ts +++ b/packages/playwright-core/src/protocol/channels.ts @@ -397,7 +397,8 @@ export type LocalUtilsHarOpenOptions = { }; export type LocalUtilsHarOpenResult = { - harId: string, + harId?: string, + error?: string, }; export type LocalUtilsHarLookupParams = { harId: string, diff --git a/packages/playwright-core/src/protocol/protocol.yml b/packages/playwright-core/src/protocol/protocol.yml index b32728de6a..21e9099549 100644 --- a/packages/playwright-core/src/protocol/protocol.yml +++ b/packages/playwright-core/src/protocol/protocol.yml @@ -482,7 +482,8 @@ LocalUtils: parameters: file: string returns: - harId: string + harId: string? + error: string? harLookup: parameters: diff --git a/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts b/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts index 9f5f60a56e..a797fe4978 100644 --- a/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts @@ -95,12 +95,16 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels. let harBackend: HarBackend; if (params.file.endsWith('.zip')) { const zipFile = new ZipFile(params.file); - const har = await zipFile.read('har.har'); + const entryNames = await zipFile.entries(); + const harEntryName = entryNames.find(e => e.endsWith('.har')); + if (!harEntryName) + return { error: 'Specified archive does not have a .har file' }; + const har = await zipFile.read(harEntryName); const harFile = JSON.parse(har.toString()) as HARFile; - harBackend = new HarBackend(harFile, zipFile); + harBackend = new HarBackend(harFile, null, zipFile); } else { const harFile = JSON.parse(await fs.promises.readFile(params.file, 'utf-8')) as HARFile; - harBackend = new HarBackend(harFile, null); + harBackend = new HarBackend(harFile, path.dirname(params.file), null); } this._harBakends.set(harBackend.id, harBackend); return { harId: harBackend.id }; @@ -128,9 +132,11 @@ class HarBackend { readonly id = createGuid(); private _harFile: HARFile; private _zipFile: ZipFile | null; + private _baseDir: string | null; - constructor(harFile: HARFile, zipFile: ZipFile | null) { + constructor(harFile: HARFile, baseDir: string | null, zipFile: ZipFile | null) { this._harFile = harFile; + this._baseDir = baseDir; this._zipFile = zipFile; } @@ -158,21 +164,22 @@ class HarBackend { const response = entry.response; const sha1 = (response.content as any)._sha1; - let body: string | undefined; - let base64Encoded = false; + let body = response.content.text; + let base64Encoded = response.content.encoding === 'base64'; - if (this._zipFile && sha1) { - const buffer = await this._zipFile.read(sha1).catch(() => { - return { action: 'error', message: `Malformed HAR: payload ${sha1} for request ${url} is not found in archive` }; - }); - - if (buffer) { - body = buffer.toString('base64'); - base64Encoded = true; + if (sha1) { + let buffer: Buffer; + try { + if (this._zipFile) + buffer = await this._zipFile.read(sha1); + else + buffer = await fs.promises.readFile(path.resolve(this._baseDir!, sha1)); + } catch (e) { + return { action: 'error', message: e.message }; } - } else { - body = response.content.text; - base64Encoded = response.content.encoding === 'base64'; + + body = buffer.toString('base64'); + base64Encoded = true; } return {