feat(network): expose bodySize, transferSize, and headerSize (#8234)

This commit is contained in:
Max Schmitt 2021-08-27 22:53:57 +02:00 committed by GitHub
parent 89245de0ef
commit f3dde0650f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 158 additions and 3 deletions

View File

@ -183,6 +183,17 @@ following: `document`, `stylesheet`, `image`, `media`, `font`, `script`, `texttr
Returns the matching [Response] object, or `null` if the response was not received due to error.
## method: Request.sizes
- returns: <[Object]>
- `requestBodySize` <[int]> Size of the request body (POST data payload) in bytes. Set to 0 if there was no body.
- `requestHeadersSize` <[float]> Total number of bytes from the start of the HTTP request message until (and including) the double CRLF before the body.
- `responseBodySize` <[int]> Size of the received response body in bytes.
- `responseHeadersSize` <[float]> Total number of bytes from the start of the HTTP response message until (and including) the double CRLF before the body.
- `responseTransferSize` <[float]> Total number of bytes received for the request.
Returns resource size information for given request. Requires the response to be finished via [`method: Response.finished`]
to ensure the info is available.
## method: Request.timing
- returns: <[Object]>
- `startTime` <[float]> Request start time in milliseconds elapsed since January 1, 1970 00:00:00 UTC

View File

@ -86,7 +86,9 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel,
});
this._channel.on('request', ({ request, page }) => this._onRequest(network.Request.from(request), Page.fromNullable(page)));
this._channel.on('requestFailed', ({ request, failureText, responseEndTiming, page }) => this._onRequestFailed(network.Request.from(request), responseEndTiming, failureText, Page.fromNullable(page)));
this._channel.on('requestFinished', ({ request, responseEndTiming, page }) => this._onRequestFinished(network.Request.from(request), responseEndTiming, Page.fromNullable(page)));
this._channel.on('requestFinished', ({ request, responseEndTiming, page, requestSizes }) =>
this._onRequestFinished(network.Request.from(request), responseEndTiming, requestSizes, Page.fromNullable(page))
);
this._channel.on('response', ({ response, page }) => this._onResponse(network.Response.from(response), Page.fromNullable(page)));
this._closedPromise = new Promise(f => this.once(Events.BrowserContext.Close, f));
}
@ -124,9 +126,10 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel,
page.emit(Events.Page.RequestFailed, request);
}
private _onRequestFinished(request: network.Request, responseEndTiming: number, page: Page | null) {
private _onRequestFinished(request: network.Request, responseEndTiming: number, requestSizes: channels.RequestSizes, page: Page | null) {
if (request._timing)
request._timing.responseEnd = responseEndTiming;
request._sizes = requestSizes;
this.emit(Events.BrowserContext.RequestFinished, request);
if (page)
page.emit(Events.Page.RequestFinished, request);

View File

@ -59,6 +59,7 @@ export class Request extends ChannelOwner<channels.RequestChannel, channels.Requ
_headers: Headers;
private _postData: Buffer | null;
_timing: ResourceTiming;
_sizes: RequestSizes = { requestBodySize: 0, requestHeadersSize: 0, responseBodySize: 0, responseHeadersSize: 0, responseTransferSize: 0 };
static from(request: channels.RequestChannel): Request {
return (request as any)._object;
@ -167,6 +168,10 @@ export class Request extends ChannelOwner<channels.RequestChannel, channels.Requ
return this._timing;
}
sizes(): RequestSizes {
return this._sizes;
}
_finalRequest(): Request {
return this._redirectedTo ? this._redirectedTo._finalRequest() : this;
}
@ -368,6 +373,14 @@ export type ResourceTiming = {
responseEnd: number;
};
export type RequestSizes = {
requestBodySize: number;
requestHeadersSize: number;
responseBodySize: number;
responseHeadersSize: number;
responseTransferSize: number;
};
export class Response extends ChannelOwner<channels.ResponseChannel, channels.ResponseInitializer> implements api.Response {
private _headers: Headers;
private _request: Request;

View File

@ -86,7 +86,8 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
context.on(BrowserContext.Events.RequestFinished, (request: Request) => this._dispatchEvent('requestFinished', {
request: RequestDispatcher.from(scope, request),
responseEndTiming: request._responseEndTiming,
page: PageDispatcher.fromNullable(this._scope, request.frame()._page.initializedOrUndefined())
requestSizes: request.sizes(),
page: PageDispatcher.fromNullable(this._scope, request.frame()._page.initializedOrUndefined()),
}));
}

View File

@ -766,6 +766,7 @@ export type BrowserContextRequestFailedEvent = {
export type BrowserContextRequestFinishedEvent = {
request: RequestChannel,
responseEndTiming: number,
requestSizes: RequestSizes,
page?: PageChannel,
};
export type BrowserContextResponseEvent = {
@ -2638,6 +2639,14 @@ export type SecurityDetails = {
validTo?: number,
};
export type RequestSizes = {
requestBodySize: number,
requestHeadersSize: number,
responseBodySize: number,
responseHeadersSize: number,
responseTransferSize: number,
};
export type RemoteAddr = {
ipAddress: string,
port: number,

View File

@ -756,6 +756,7 @@ BrowserContext:
parameters:
request: Request
responseEndTiming: number
requestSizes: RequestSizes
page: Page?
response:
@ -2232,6 +2233,15 @@ SecurityDetails:
validFrom: number?
validTo: number?
RequestSizes:
type: object
properties:
requestBodySize: number
requestHeadersSize: number
responseBodySize: number
responseHeadersSize: number
responseTransferSize: number
RemoteAddr:
type: object

View File

@ -1055,6 +1055,13 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
validFrom: tOptional(tNumber),
validTo: tOptional(tNumber),
});
scheme.RequestSizes = tObject({
requestBodySize: tNumber,
requestHeadersSize: tNumber,
responseBodySize: tNumber,
responseHeadersSize: tNumber,
responseTransferSize: tNumber,
});
scheme.RemoteAddr = tObject({
ipAddress: tString,
port: tNumber,

View File

@ -215,6 +215,16 @@ export class Request extends SdkObject {
headersSize += header.name.length + header.value.length + 4; // 4 = ': ' + '\r\n'
return headersSize;
}
sizes() {
return {
requestBodySize: this.bodySize(),
requestHeadersSize: this.headersSize(),
responseBodySize: this._sizes.responseBodySize,
responseHeadersSize: this._existingResponse()!.headersSize(),
responseTransferSize: this._sizes.transferSize,
};
}
}
export class Route extends SdkObject {

View File

@ -265,3 +265,63 @@ it('should return navigation bit when navigating to image', async ({page, server
await page.goto(server.PREFIX + '/pptr.png');
expect(requests[0].isNavigationRequest()).toBe(true);
});
it('should set bodySize and headersSize', async ({page, server,browserName, platform}) => {
await page.goto(server.EMPTY_PAGE);
const [request] = await Promise.all([
page.waitForEvent('request'),
page.evaluate(() => fetch('./get', { method: 'POST', body: '12345'}).then(r => r.text())),
]);
await (await request.response()).finished();
expect(request.sizes().requestBodySize).toBe(5);
expect(request.sizes().requestHeadersSize).toBeGreaterThanOrEqual(300);
});
it('should should set bodySize to 0 if there was no body', async ({page, server,browserName, platform}) => {
await page.goto(server.EMPTY_PAGE);
const [request] = await Promise.all([
page.waitForEvent('request'),
page.evaluate(() => fetch('./get').then(r => r.text())),
]);
await (await request.response()).finished();
expect(request.sizes().requestBodySize).toBe(0);
expect(request.sizes().requestHeadersSize).toBeGreaterThanOrEqual(250);
});
it('should should set bodySize, headersSize, and transferSize', async ({page, server, browserName, platform}) => {
server.setRoute('/get', (req, res) => {
// In Firefox, |fetch| will be hanging until it receives |Content-Type| header
// from server.
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('abc134');
});
await page.goto(server.EMPTY_PAGE);
const [response] = await Promise.all([
page.waitForEvent('response'),
page.evaluate(async () => fetch('./get').then(r => r.text())),
server.waitForRequest('/get'),
]);
await response.finished();
expect(response.request().sizes().responseBodySize).toBe(6);
expect(response.request().sizes().responseHeadersSize).toBeGreaterThanOrEqual(150);
expect(response.request().sizes().responseTransferSize).toBeGreaterThanOrEqual(160);
});
it('should should set bodySize to 0 when there was no response body', async ({page, server, browserName, platform}) => {
server.setRoute('/get', (req, res) => {
// In Firefox, |fetch| will be hanging until it receives |Content-Type| header
// from server.
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('');
});
await page.goto(server.EMPTY_PAGE);
const [response] = await Promise.all([
page.waitForEvent('response'),
page.evaluate(async () => fetch('./get').then(r => r.text())),
server.waitForRequest('/get'),
]);
await response.finished();
expect(response.request().sizes().responseBodySize).toBe(0);
expect(response.request().sizes().responseHeadersSize).toBeGreaterThanOrEqual(150);
expect(response.request().sizes().responseTransferSize).toBeGreaterThanOrEqual(160);
});

31
types/types.d.ts vendored
View File

@ -11668,6 +11668,37 @@ export interface Request {
*/
response(): Promise<null|Response>;
/**
* Returns resource size information for given request. Requires the response to be finished via
* [response.finished()](https://playwright.dev/docs/api/class-response#response-finished) to ensure the info is available.
*/
sizes(): {
/**
* Size of the request body (POST data payload) in bytes. Set to 0 if there was no body.
*/
requestBodySize: number;
/**
* Total number of bytes from the start of the HTTP request message until (and including) the double CRLF before the body.
*/
requestHeadersSize: number;
/**
* Size of the received response body in bytes.
*/
responseBodySize: number;
/**
* Total number of bytes from the start of the HTTP response message until (and including) the double CRLF before the body.
*/
responseHeadersSize: number;
/**
* Total number of bytes received for the request.
*/
responseTransferSize: number;
};
/**
* Returns resource timing information for given request. Most of the timing values become available upon the response,
* `responseEnd` becomes available when request finishes. Find more information at