diff --git a/docs/src/api/class-request.md b/docs/src/api/class-request.md index 137f8c6b66..3a909b280d 100644 --- a/docs/src/api/class-request.md +++ b/docs/src/api/class-request.md @@ -188,7 +188,7 @@ 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 +## async 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` <[int]> Total number of bytes from the start of the HTTP request message until (and including) the double CRLF before the body. diff --git a/src/client/browserContext.ts b/src/client/browserContext.ts index 538c78afa3..729423530b 100644 --- a/src/client/browserContext.ts +++ b/src/client/browserContext.ts @@ -125,13 +125,12 @@ export class BrowserContext extends ChannelOwner | undefined; 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; @@ -187,8 +186,13 @@ export class Request extends ChannelOwner { + const response = await this.response(); + if (!response) + throw new Error('Unable to fetch sizes for failed request'); + return response._wrapApiCall(async (channel: channels.ResponseChannel) => { + return (await channel.sizes()).sizes; + }); } _finalRequest(): Request { diff --git a/src/dispatchers/browserContextDispatcher.ts b/src/dispatchers/browserContextDispatcher.ts index 588d71795b..f0b975e51f 100644 --- a/src/dispatchers/browserContextDispatcher.ts +++ b/src/dispatchers/browserContextDispatcher.ts @@ -87,7 +87,6 @@ export class BrowserContextDispatcher extends Dispatcher { return { headers: await this._object.rawResponseHeaders() }; } + + async sizes(params?: channels.ResponseSizesParams, metadata?: channels.Metadata): Promise { + return { sizes: await this._object.sizes() }; + } } export class RouteDispatcher extends Dispatcher implements channels.RouteChannel { diff --git a/src/protocol/channels.ts b/src/protocol/channels.ts index d38e94a9b9..e565d1684e 100644 --- a/src/protocol/channels.ts +++ b/src/protocol/channels.ts @@ -802,7 +802,6 @@ export type BrowserContextRequestFinishedEvent = { request: RequestChannel, response?: ResponseChannel, responseEndTiming: number, - requestSizes: RequestSizes, page?: PageChannel, }; export type BrowserContextResponseEvent = { @@ -2698,6 +2697,7 @@ export interface ResponseChannel extends Channel { serverAddr(params?: ResponseServerAddrParams, metadata?: Metadata): Promise; rawRequestHeaders(params?: ResponseRawRequestHeadersParams, metadata?: Metadata): Promise; rawResponseHeaders(params?: ResponseRawResponseHeadersParams, metadata?: Metadata): Promise; + sizes(params?: ResponseSizesParams, metadata?: Metadata): Promise; } export type ResponseBodyParams = {}; export type ResponseBodyOptions = {}; @@ -2724,6 +2724,11 @@ export type ResponseRawResponseHeadersOptions = {}; export type ResponseRawResponseHeadersResult = { headers: NameValue[], }; +export type ResponseSizesParams = {}; +export type ResponseSizesOptions = {}; +export type ResponseSizesResult = { + sizes: RequestSizes, +}; export interface ResponseEvents { } diff --git a/src/protocol/protocol.yml b/src/protocol/protocol.yml index 57189f3836..290af90bce 100644 --- a/src/protocol/protocol.yml +++ b/src/protocol/protocol.yml @@ -766,7 +766,6 @@ BrowserContext: request: Request response: Response? responseEndTiming: number - requestSizes: RequestSizes page: Page? response: @@ -2226,6 +2225,10 @@ Response: type: array items: NameValue + sizes: + returns: + sizes: RequestSizes + SecurityDetails: type: object diff --git a/src/protocol/validator.ts b/src/protocol/validator.ts index 59b8c9334a..82659c0cea 100644 --- a/src/protocol/validator.ts +++ b/src/protocol/validator.ts @@ -1055,6 +1055,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { scheme.ResponseServerAddrParams = tOptional(tObject({})); scheme.ResponseRawRequestHeadersParams = tOptional(tObject({})); scheme.ResponseRawResponseHeadersParams = tOptional(tObject({})); + scheme.ResponseSizesParams = tOptional(tObject({})); scheme.SecurityDetails = tObject({ issuer: tOptional(tString), protocol: tOptional(tString), diff --git a/src/server/chromium/crNetworkManager.ts b/src/server/chromium/crNetworkManager.ts index 5c4b42c77b..200b2b2b31 100644 --- a/src/server/chromium/crNetworkManager.ts +++ b/src/server/chromium/crNetworkManager.ts @@ -51,6 +51,7 @@ export class CRNetworkManager { return [ eventsHelper.addEventListener(session, 'Fetch.requestPaused', this._onRequestPaused.bind(this, workerFrame)), eventsHelper.addEventListener(session, 'Fetch.authRequired', this._onAuthRequired.bind(this)), + eventsHelper.addEventListener(session, 'Network.dataReceived', this._onDataReceived.bind(this)), eventsHelper.addEventListener(session, 'Network.requestWillBeSent', this._onRequestWillBeSent.bind(this, workerFrame)), eventsHelper.addEventListener(session, 'Network.requestWillBeSentExtraInfo', this._onRequestWillBeSentExtraInfo.bind(this)), eventsHelper.addEventListener(session, 'Network.responseReceived', this._onResponseReceived.bind(this)), @@ -350,6 +351,12 @@ export class CRNetworkManager { this._page._frameManager.requestReceivedResponse(response); } + _onDataReceived(event: Protocol.Network.dataReceivedPayload) { + const request = this._requestIdToRequest.get(event.requestId); + if (request) + request.request.responseSize.bodySize += event.dataLength; + } + _onLoadingFinished(event: Protocol.Network.loadingFinishedPayload) { this._responseExtraInfoTracker.loadingFinished(event); @@ -365,8 +372,7 @@ export class CRNetworkManager { // event from protocol. @see https://crbug.com/883475 const response = request.request._existingResponse(); if (response) { - request.request._sizes.transferSize = event.encodedDataLength; - request.request._sizes.responseBodySize = event.encodedDataLength - response?.headersSize(); + request.request.responseSize.transferSize = event.encodedDataLength; response._requestFinished(helper.secondsToRoundishMillis(event.timestamp - request._timestamp)); } this._requestIdToRequest.delete(request._requestId); diff --git a/src/server/firefox/ffNetworkManager.ts b/src/server/firefox/ffNetworkManager.ts index 8017b3c989..ef7fa9b304 100644 --- a/src/server/firefox/ffNetworkManager.ts +++ b/src/server/firefox/ffNetworkManager.ts @@ -116,9 +116,7 @@ export class FFNetworkManager { if (!request) return; const response = request.request._existingResponse()!; - - request.request._sizes.transferSize = event.transferSize; - request.request._sizes.responseBodySize = event.transferSize - response.headersSize(); + request.request.responseSize.transferSize = event.transferSize; // Keep redirected requests in the map for future reference as redirectedFrom. const isRedirected = response.status() >= 300 && response.status() <= 399; diff --git a/src/server/network.ts b/src/server/network.ts index a45ba3aba2..e2b0731dc4 100644 --- a/src/server/network.ts +++ b/src/server/network.ts @@ -80,8 +80,9 @@ export function stripFragmentFromUrl(url: string): string { return url.substring(0, url.indexOf('#')); } -type RequestSizes = { - responseBodySize: number; +type ResponseSize = { + bodySize: number; + encodedBodySize: number; transferSize: number; }; @@ -101,7 +102,7 @@ export class Request extends SdkObject { private _frame: frames.Frame; private _waitForResponsePromise = new ManualPromise(); _responseEndTiming = -1; - _sizes: RequestSizes = { responseBodySize: 0, transferSize: 0 }; + readonly responseSize: ResponseSize = { bodySize: 0, encodedBodySize: 0, transferSize: 0 }; constructor(frame: frames.Frame, redirectedFrom: Request | null, documentId: string | undefined, url: string, resourceType: string, method: string, postData: Buffer | null, headers: types.HeadersArray) { @@ -195,28 +196,6 @@ export class Request extends SdkObject { bodySize(): number { return this.postDataBuffer()?.length || 0; } - - headersSize(): number { - if (!this._response) - return 0; - let headersSize = 4; // 4 = 2 spaces + 2 line breaks (GET /path \r\n) - headersSize += this.method().length; - headersSize += (new URL(this.url())).pathname.length; - headersSize += 8; // httpVersion - for (const header of this._headers) - 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 { @@ -297,6 +276,14 @@ export type ResourceTiming = { responseStart: number; }; +export type ResourceSizes = { + requestBodySize: number, + requestHeadersSize: number, + responseBodySize: number, + responseHeadersSize: number, + responseTransferSize: number, +}; + export type RemoteAddr = { ipAddress: string; port: number; @@ -435,14 +422,6 @@ export class Response extends SdkObject { return this._request.frame(); } - transferSize(): number | undefined { - return this._request._sizes.transferSize; - } - - bodySize(): number { - return this._request._sizes.responseBodySize; - } - httpVersion(): string { if (!this._httpVersion) return 'HTTP/1.1'; @@ -451,16 +430,52 @@ export class Response extends SdkObject { return this._httpVersion; } - headersSize(): number { + private async _requestHeadersSize(): Promise { + let headersSize = 4; // 4 = 2 spaces + 2 line breaks (GET /path \r\n) + headersSize += this._request.method().length; + headersSize += (new URL(this.url())).pathname.length; + headersSize += 8; // httpVersion + const headers = this._rawRequestHeadersPromise ? await this._rawRequestHeadersPromise : this._request._headers; + for (const header of headers) + headersSize += header.name.length + header.value.length + 4; // 4 = ': ' + '\r\n' + return headersSize; + } + + private async _responseHeadersSize(): Promise { let headersSize = 4; // 4 = 2 spaces + 2 line breaks (HTTP/1.1 200 Ok\r\n) headersSize += 8; // httpVersion; headersSize += 3; // statusCode; headersSize += this.statusText().length; - for (const header of this.headers()) + const headers = this._rawResponseHeadersPromise ? await this._rawResponseHeadersPromise : this._headers; + for (const header of headers) headersSize += header.name.length + header.value.length + 4; // 4 = ': ' + '\r\n' headersSize += 2; // '\r\n' return headersSize; } + + async sizes(): Promise { + await this._finishedPromise; + const requestHeadersSize = await this._requestHeadersSize(); + const responseHeadersSize = await this._responseHeadersSize(); + let { bodySize, encodedBodySize, transferSize } = this._request.responseSize; + if (!encodedBodySize && transferSize) { + // Chromium only populates transferSize + encodedBodySize = transferSize - responseHeadersSize; + // Firefox only populate transferSize. + if (!bodySize) + bodySize = encodedBodySize; + } else if (!transferSize) { + // WebKit does not provide transfer size. + transferSize = encodedBodySize + responseHeadersSize; + } + return { + requestBodySize: this._request.bodySize(), + requestHeadersSize, + responseBodySize: bodySize, + responseHeadersSize, + responseTransferSize: transferSize, + }; + } } export class InterceptedResponse extends SdkObject { diff --git a/src/server/supplements/har/harTracer.ts b/src/server/supplements/har/harTracer.ts index 1de0da2187..0760c4a352 100644 --- a/src/server/supplements/har/harTracer.ts +++ b/src/server/supplements/har/harTracer.ts @@ -202,19 +202,11 @@ export class HarTracer { return; const httpVersion = response.httpVersion(); - const transferSize = response.transferSize() || -1; - const responseHeadersSize = response.headersSize(); - harEntry.request.httpVersion = httpVersion; - harEntry.response.bodySize = response.bodySize(); - harEntry.response.headersSize = responseHeadersSize; - harEntry.response._transferSize = transferSize; - harEntry.request.headersSize = request.headersSize(); const promise = response.body().then(buffer => { const content = harEntry.response.content; content.size = buffer.length; - content.compression = harEntry.response.bodySize !== -1 ? buffer.length - harEntry.response.bodySize : 0; if (buffer && buffer.length > 0) { if (this._options.content === 'embedded') { content.text = buffer.toString('base64'); @@ -236,6 +228,14 @@ export class HarTracer { this._delegate.onEntryFinished(harEntry); }); this._addBarrier(page, promise); + this._addBarrier(page, response.sizes().then(async sizes => { + harEntry.response.bodySize = sizes.responseBodySize; + harEntry.response.headersSize = sizes.responseHeadersSize; + harEntry.response._transferSize = sizes.responseTransferSize; + harEntry.request.headersSize = sizes.requestHeadersSize; + const content = harEntry.response.content; + content.compression = Math.max(0, sizes.responseBodySize - sizes.responseTransferSize - sizes.responseHeadersSize); + })); } private _onResponse(response: network.Response) { diff --git a/src/server/webkit/wkPage.ts b/src/server/webkit/wkPage.ts index a2cb05a87b..75a8088039 100644 --- a/src/server/webkit/wkPage.ts +++ b/src/server/webkit/wkPage.ts @@ -1053,7 +1053,6 @@ export class WKPage implements PageDelegate { validFrom: responseReceivedPayload?.response.security?.certificate?.validFrom, validTo: responseReceivedPayload?.response.security?.certificate?.validUntil, }); - request.request._sizes.transferSize += response.headersSize(); if (event.metrics?.protocol) response._setHttpVersion(event.metrics.protocol); response._requestFinished(helper.secondsToRoundishMillis(event.timestamp - request._timestamp)); @@ -1088,8 +1087,8 @@ export class WKPage implements PageDelegate { const request = this._requestIdToRequest.get(event.requestId); if (!request) return; - request.request._sizes.responseBodySize += event.encodedDataLength === -1 ? event.dataLength : event.encodedDataLength; - request.request._sizes.transferSize += event.encodedDataLength === -1 ? event.dataLength : event.encodedDataLength; + request.request.responseSize.bodySize += event.dataLength || (event.encodedDataLength === -1 ? 0 : event.encodedDataLength); + request.request.responseSize.encodedBodySize += event.encodedDataLength !== -1 ? event.encodedDataLength : event.dataLength; } async _grantPermissions(origin: string, permissions: string[]) { diff --git a/tests/assets/simplezip.json b/tests/assets/simplezip.json new file mode 100644 index 0000000000..5efbbae5a5 --- /dev/null +++ b/tests/assets/simplezip.json @@ -0,0 +1,340 @@ +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} +{"foo": "bar"} diff --git a/tests/har.spec.ts b/tests/har.spec.ts index f25b674216..981f89381f 100644 --- a/tests/har.spec.ts +++ b/tests/har.spec.ts @@ -276,18 +276,18 @@ it('should include sizes', async ({ contextFactory, server, asset }, testInfo) = expect(log.entries[1].request.url.endsWith('one-style.css')).toBe(true); expect(log.entries[1].response.bodySize).toBe(fs.statSync(asset('one-style.css')).size); expect(log.entries[1].response.headersSize).toBeGreaterThanOrEqual(100); - expect(log.entries[1].response._transferSize).toBeGreaterThanOrEqual(200); + expect(log.entries[1].response._transferSize).toBeGreaterThanOrEqual(150); }); it('should work with gzip compression', async ({ contextFactory, server, browserName }, testInfo) => { - it.fixme(browserName === 'webkit'); + it.fixme(browserName !== 'chromium'); const { page, getLog } = await pageWithHar(contextFactory, testInfo); - server.enableGzip('/simple.json'); - const response = await page.goto(server.PREFIX + '/simple.json'); + server.enableGzip('/simplezip.json'); + const response = await page.goto(server.PREFIX + '/simplezip.json'); expect(response.headers()['content-encoding']).toBe('gzip'); const log = await getLog(); expect(log.entries.length).toBe(1); - expect(log.entries[0].response.content.compression).toBe(-20); + expect(log.entries[0].response.content.compression).toBeGreaterThan(4000); }); it('should calculate time', async ({ contextFactory, server }, testInfo) => { diff --git a/tests/page/page-network-request.spec.ts b/tests/page/page-network-request.spec.ts index 03232951f1..57bbe44ca8 100644 --- a/tests/page/page-network-request.spec.ts +++ b/tests/page/page-network-request.spec.ts @@ -273,55 +273,6 @@ it('should return navigation bit when navigating to image', async ({page, server 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(250); -}); - -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(200); -}); - -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}) => { - const response = await page.goto(server.EMPTY_PAGE); - await response.finished(); - expect(response.request().sizes().responseBodySize).toBe(0); - expect(response.request().sizes().responseHeadersSize).toBeGreaterThanOrEqual(150); - expect(response.request().sizes().responseTransferSize).toBeGreaterThanOrEqual(160); -}); - it('should report raw headers', async ({ page, server, browserName }) => { const response = await page.goto(server.EMPTY_PAGE); const requestHeaders = await response.request().rawHeaders(); diff --git a/tests/page/page-network-sizes.spec.ts b/tests/page/page-network-sizes.spec.ts new file mode 100644 index 0000000000..42db1290be --- /dev/null +++ b/tests/page/page-network-sizes.spec.ts @@ -0,0 +1,68 @@ +/** + * Copyright 2018 Google Inc. All rights reserved. + * Modifications copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test as it, expect } from './pageTest'; + +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(); + const sizes = await request.sizes(); + expect(sizes.requestBodySize).toBe(5); + expect(sizes.requestHeadersSize).toBeGreaterThanOrEqual(250); +}); + +it('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())), + ]); + const sizes = await request.sizes(); + expect(sizes.requestBodySize).toBe(0); + expect(sizes.requestHeadersSize).toBeGreaterThanOrEqual(200); +}); + +it('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'), + ]); + const sizes = await response.request().sizes(); + expect(sizes.responseBodySize).toBe(6); + expect(sizes.responseHeadersSize).toBeGreaterThanOrEqual(150); + expect(sizes.responseTransferSize).toBeGreaterThanOrEqual(160); +}); + +it('should set bodySize to 0 when there was no response body', async ({page, server, browserName, platform}) => { + const response = await page.goto(server.EMPTY_PAGE); + const sizes = await response.request().sizes(); + expect(sizes.responseBodySize).toBe(0); + expect(sizes.responseHeadersSize).toBeGreaterThanOrEqual(150); + expect(sizes.responseTransferSize).toBeGreaterThanOrEqual(160); +}); diff --git a/types/types.d.ts b/types/types.d.ts index d0f20410f9..7e9880e472 100644 --- a/types/types.d.ts +++ b/types/types.d.ts @@ -13133,7 +13133,7 @@ export interface Request { * 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(): { + sizes(): Promise<{ /** * Size of the request body (POST data payload) in bytes. Set to 0 if there was no body. */ @@ -13158,7 +13158,7 @@ export interface Request { * 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,