Skip to content

Commit

Permalink
fix: handle page close during response event
Browse files Browse the repository at this point in the history
  • Loading branch information
vigneshshanmugam committed Oct 13, 2021
1 parent ffafe21 commit ddafedb
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 40 deletions.
2 changes: 1 addition & 1 deletion __tests__/plugins/network.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ describe('network', () => {
const driver = await Gatherer.setupDriver({ wsEndpoint });
const network = new NetworkManager(driver);
await network.start();
await driver.page.goto(server.TEST_PAGE, { waitUntil: 'networkidle' });
await driver.page.goto(server.TEST_PAGE);
const netinfo = await network.stop();
await Gatherer.stop();
expect(netinfo[0]).toMatchObject({
Expand Down
6 changes: 3 additions & 3 deletions src/common_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ export type Params = Record<string, any>;
export type NetworkConditions = {
offline: boolean;
downloadThroughput: number;
uploadThroughput: number;
latency: number;
uploadThroughput: number;
latency: number;
};
export type HooksArgs = {
env: string;
Expand Down Expand Up @@ -118,7 +118,7 @@ export type Response = {
url?: string;
statusCode: number;
statusText?: string;
mimeType: string;
mimeType?: string;
httpVersion?: string;
headers: Record<string, string>;
// Total size in bytes of the response (body and headers)
Expand Down
107 changes: 71 additions & 36 deletions src/plugins/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
*
*/

import { Request, Response } from 'playwright-chromium';
import { Page, Request, Response } from 'playwright-chromium';
import { NetworkInfo, BrowserInfo, Driver } from '../common_types';
import { Step } from '../dsl';
import { getTimestamp } from '../helpers';
Expand All @@ -49,11 +49,25 @@ type RequestWithEntry = Request & {

export class NetworkManager {
private _browser: BrowserInfo;
private _barrierPromises = new Set<Promise<void>>();
results: Array<NetworkInfo> = [];
_currentStep: Partial<Step> = null;

constructor(private driver: Driver) {}

private _addBarrier(page: Page, promise: Promise<void>) {
const race = Promise.race([
new Promise<void>(resolve =>
page.on('close', () => {
this._barrierPromises.delete(race);
resolve();
})
),
promise,
]) as Promise<void>;
this._barrierPromises.add(race);
}

async start() {
const { client, context } = this.driver;
const { product } = await client.send('Browser.getVersion');
Expand Down Expand Up @@ -96,7 +110,6 @@ export class NetworkManager {
},
response: {
statusCode: -1,
mimeType: 'x-unknown',
headers: {},
redirectURL: '',
},
Expand All @@ -122,53 +135,55 @@ export class NetworkManager {
const networkEntry = this._findNetworkEntry(request);
if (!networkEntry) return;

const server = await response.serverAddr();
const responseHeaders = await response.allHeaders();
const mimeType = responseHeaders['content-type']
? responseHeaders['content-type'].split(';')[0]
: 'unknown';

const requestHeaders = await request.allHeaders();

networkEntry.request.headers = requestHeaders;
networkEntry.request.referrer = requestHeaders?.referer;
networkEntry.status = response.status();
networkEntry.responseReceivedTime = epochTimeInSeconds();
networkEntry.response = {
url: response.url(),
statusCode: response.status(),
statusText: response.statusText(),
headers: responseHeaders,
mimeType,
headers: {},
redirectURL: networkEntry.response.redirectURL,
securityDetails: await response.securityDetails(),
remoteIPAddress: server?.ipAddress,
remotePort: server?.port,
};
networkEntry.status = response.status();
networkEntry.responseReceivedTime = epochTimeInSeconds();

const page = request.frame().page();
this._addBarrier(
page,
request.allHeaders().then(reqHeaders => {
networkEntry.request.headers = reqHeaders;
networkEntry.request.referrer = reqHeaders?.referer;
})
);
this._addBarrier(
page,
response.serverAddr().then(server => {
networkEntry.response.remoteIPAddress = server?.ipAddress;
networkEntry.response.remotePort = server?.port;
})
);
this._addBarrier(
page,
response.securityDetails().then(details => {
networkEntry.response.securityDetails = details;
})
);
this._addBarrier(
page,
response.allHeaders().then(resHeaders => {
networkEntry.response.headers = resHeaders;

const mimeType = resHeaders['content-type']
? resHeaders['content-type'].split(';')[0]
: 'unknown';
networkEntry.response.mimeType = mimeType;
})
);
}

private async _onRequestCompleted(request: Request) {
const networkEntry = this._findNetworkEntry(request);
if (!networkEntry) return;

networkEntry.loadEndTime = epochTimeInSeconds();

// For aborted/failed requests sizes does not exist
const sizes = await request.sizes().catch(() => {});
if (sizes) {
networkEntry.request.bytes =
sizes.requestHeadersSize + sizes.requestBodySize;
networkEntry.request.body = {
bytes: sizes.requestBodySize,
};
networkEntry.response.bytes =
sizes.responseHeadersSize + sizes.responseBodySize;
networkEntry.response.body = {
bytes: sizes.responseBodySize,
};
networkEntry.transferSize = sizes.responseBodySize;
}

const timing = request.timing();
const { loadEndTime, requestSentTime } = networkEntry;
networkEntry.timings = {
Expand Down Expand Up @@ -243,6 +258,25 @@ export class NetworkManager {
receive,
total: roundMilliSecs(total),
};

const page = request.frame().page();
// For aborted/failed requests sizes does not exist
this._addBarrier(
page,
request.sizes().then(sizes => {
networkEntry.request.bytes =
sizes.requestHeadersSize + sizes.requestBodySize;
networkEntry.request.body = {
bytes: sizes.requestBodySize,
};
networkEntry.response.bytes =
sizes.responseHeadersSize + sizes.responseBodySize;
networkEntry.response.body = {
bytes: sizes.responseBodySize,
};
networkEntry.transferSize = sizes.responseBodySize;
})
);
}

stop() {
Expand All @@ -251,6 +285,7 @@ export class NetworkManager {
context.on('response', this._onResponse.bind(this));
context.on('requestfinished', this._onRequestCompleted.bind(this));
context.on('requestfailed', this._onRequestCompleted.bind(this));
this._barrierPromises.clear();
return this.results;
}
}

0 comments on commit ddafedb

Please sign in to comment.