perf_hooks: add deliveryType and responseStatus fields

PR-URL: https://github.com/nodejs/node/pull/51589
Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br>
Reviewed-By: Yagiz Nizipli <yagiz.nizipli@sentry.io>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Minwoo Jung <nodecorelab@gmail.com>
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
This commit is contained in:
Khafra 2024-05-12 14:02:48 -04:00 committed by GitHub
parent 7b2dc79437
commit e2697c1a64
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 96 additions and 24 deletions

View File

@ -246,12 +246,16 @@ and can be queried with `performance.getEntries`,
observation is performed, the entries should be cleared from the global observation is performed, the entries should be cleared from the global
Performance Timeline manually with `performance.clearMarks`. Performance Timeline manually with `performance.clearMarks`.
### `performance.markResourceTiming(timingInfo, requestedUrl, initiatorType, global, cacheMode)` ### `performance.markResourceTiming(timingInfo, requestedUrl, initiatorType, global, cacheMode, bodyInfo, responseStatus[, deliveryType])`
<!-- YAML <!-- YAML
added: added:
- v18.2.0 - v18.2.0
- v16.17.0 - v16.17.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/51589
description: Added bodyInfo, responseStatus, and deliveryType arguments.
--> -->
* `timingInfo` {Object} [Fetch Timing Info][] * `timingInfo` {Object} [Fetch Timing Info][]
@ -259,6 +263,9 @@ added:
* `initiatorType` {string} The initiator name, e.g: 'fetch' * `initiatorType` {string} The initiator name, e.g: 'fetch'
* `global` {Object} * `global` {Object}
* `cacheMode` {string} The cache mode must be an empty string ('') or 'local' * `cacheMode` {string} The cache mode must be an empty string ('') or 'local'
* `bodyInfo` {Object} [Fetch Response Body Info][]
* `responseStatus` {number} The response's status code
* `deliveryType` {string} The delivery type. **Default:** `''`.
_This property is an extension by Node.js. It is not available in Web browsers._ _This property is an extension by Node.js. It is not available in Web browsers._
@ -1911,6 +1918,7 @@ dns.promises.resolve('localhost');
``` ```
[Async Hooks]: async_hooks.md [Async Hooks]: async_hooks.md
[Fetch Response Body Info]: https://fetch.spec.whatwg.org/#response-body-info
[Fetch Timing Info]: https://fetch.spec.whatwg.org/#fetch-timing-info [Fetch Timing Info]: https://fetch.spec.whatwg.org/#fetch-timing-info
[High Resolution Time]: https://www.w3.org/TR/hr-time-2 [High Resolution Time]: https://www.w3.org/TR/hr-time-2
[Performance Timeline]: https://w3c.github.io/performance-timeline/ [Performance Timeline]: https://w3c.github.io/performance-timeline/

View File

@ -21,6 +21,8 @@ const kCacheMode = Symbol('kCacheMode');
const kRequestedUrl = Symbol('kRequestedUrl'); const kRequestedUrl = Symbol('kRequestedUrl');
const kTimingInfo = Symbol('kTimingInfo'); const kTimingInfo = Symbol('kTimingInfo');
const kInitiatorType = Symbol('kInitiatorType'); const kInitiatorType = Symbol('kInitiatorType');
const kDeliveryType = Symbol('kDeliveryType');
const kResponseStatus = Symbol('kResponseStatus');
class PerformanceResourceTiming extends PerformanceEntry { class PerformanceResourceTiming extends PerformanceEntry {
constructor(skipThrowSymbol = undefined, name = undefined, type = undefined) { constructor(skipThrowSymbol = undefined, name = undefined, type = undefined) {
@ -136,6 +138,16 @@ class PerformanceResourceTiming extends PerformanceEntry {
return this[kTimingInfo].encodedBodySize + 300; return this[kTimingInfo].encodedBodySize + 300;
} }
get deliveryType() {
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
return this[kDeliveryType];
}
get responseStatus() {
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
return this[kResponseStatus];
}
toJSON() { toJSON() {
validateInternalField(this, kInitiatorType, 'PerformanceResourceTiming'); validateInternalField(this, kInitiatorType, 'PerformanceResourceTiming');
return { return {
@ -160,6 +172,8 @@ class PerformanceResourceTiming extends PerformanceEntry {
transferSize: this.transferSize, transferSize: this.transferSize,
encodedBodySize: this.encodedBodySize, encodedBodySize: this.encodedBodySize,
decodedBodySize: this.decodedBodySize, decodedBodySize: this.decodedBodySize,
deliveryType: this.deliveryType,
responseStatus: this.responseStatus,
}; };
} }
} }
@ -182,6 +196,8 @@ ObjectDefineProperties(PerformanceResourceTiming.prototype, {
transferSize: kEnumerableProperty, transferSize: kEnumerableProperty,
encodedBodySize: kEnumerableProperty, encodedBodySize: kEnumerableProperty,
decodedBodySize: kEnumerableProperty, decodedBodySize: kEnumerableProperty,
deliveryType: kEnumerableProperty,
responseStatus: kEnumerableProperty,
toJSON: kEnumerableProperty, toJSON: kEnumerableProperty,
[SymbolToStringTag]: { [SymbolToStringTag]: {
__proto__: null, __proto__: null,
@ -190,7 +206,15 @@ ObjectDefineProperties(PerformanceResourceTiming.prototype, {
}, },
}); });
function createPerformanceResourceTiming(requestedUrl, initiatorType, timingInfo, cacheMode = '') { function createPerformanceResourceTiming(
requestedUrl,
initiatorType,
timingInfo,
cacheMode = '',
bodyInfo,
responseStatus,
deliveryType,
) {
const resourceTiming = new PerformanceResourceTiming(kSkipThrow, requestedUrl, 'resource'); const resourceTiming = new PerformanceResourceTiming(kSkipThrow, requestedUrl, 'resource');
resourceTiming[kInitiatorType] = initiatorType; resourceTiming[kInitiatorType] = initiatorType;
@ -200,6 +224,8 @@ function createPerformanceResourceTiming(requestedUrl, initiatorType, timingInfo
// The spec doesn't say to validate it in the class construction. // The spec doesn't say to validate it in the class construction.
resourceTiming[kTimingInfo] = timingInfo; resourceTiming[kTimingInfo] = timingInfo;
resourceTiming[kCacheMode] = cacheMode; resourceTiming[kCacheMode] = cacheMode;
resourceTiming[kDeliveryType] = deliveryType;
resourceTiming[kResponseStatus] = responseStatus;
return resourceTiming; return resourceTiming;
} }
@ -211,6 +237,9 @@ function markResourceTiming(
initiatorType, initiatorType,
global, global,
cacheMode, cacheMode,
bodyInfo,
responseStatus,
deliveryType = '',
) { ) {
// https://w3c.github.io/resource-timing/#dfn-setup-the-resource-timing-entry // https://w3c.github.io/resource-timing/#dfn-setup-the-resource-timing-entry
assert( assert(
@ -222,6 +251,9 @@ function markResourceTiming(
initiatorType, initiatorType,
timingInfo, timingInfo,
cacheMode, cacheMode,
bodyInfo,
responseStatus,
deliveryType,
); );
enqueue(resource); enqueue(resource);

View File

@ -86,6 +86,9 @@ function createTimingInfo({
initiatorType, initiatorType,
customGlobal, customGlobal,
cacheMode, cacheMode,
{},
200,
'',
); );
assert(resource instanceof PerformanceEntry); assert(resource instanceof PerformanceEntry);
@ -128,6 +131,9 @@ function createTimingInfo({
initiatorType, initiatorType,
customGlobal, customGlobal,
cacheMode, cacheMode,
{},
200,
'',
); );
assert(resource instanceof PerformanceEntry); assert(resource instanceof PerformanceEntry);
@ -155,6 +161,8 @@ function createTimingInfo({
assert.strictEqual(resource.encodedBodySize, 0); assert.strictEqual(resource.encodedBodySize, 0);
assert.strictEqual(resource.decodedBodySize, 0); assert.strictEqual(resource.decodedBodySize, 0);
assert.strictEqual(resource.transferSize, 0); assert.strictEqual(resource.transferSize, 0);
assert.strictEqual(resource.deliveryType, '');
assert.strictEqual(resource.responseStatus, 200);
assert.deepStrictEqual(resource.toJSON(), { assert.deepStrictEqual(resource.toJSON(), {
name: requestedUrl, name: requestedUrl,
entryType: 'resource', entryType: 'resource',
@ -177,6 +185,8 @@ function createTimingInfo({
transferSize: 0, transferSize: 0,
encodedBodySize: 0, encodedBodySize: 0,
decodedBodySize: 0, decodedBodySize: 0,
responseStatus: 200,
deliveryType: '',
}); });
assert.strictEqual(util.inspect(performance.getEntries()), `[ assert.strictEqual(util.inspect(performance.getEntries()), `[
PerformanceResourceTiming { PerformanceResourceTiming {
@ -200,7 +210,9 @@ function createTimingInfo({
responseEnd: 0, responseEnd: 0,
transferSize: 0, transferSize: 0,
encodedBodySize: 0, encodedBodySize: 0,
decodedBodySize: 0 decodedBodySize: 0,
deliveryType: '',
responseStatus: 200
} }
]`); ]`);
assert.strictEqual(util.inspect(resource), `PerformanceResourceTiming { assert.strictEqual(util.inspect(resource), `PerformanceResourceTiming {
@ -224,7 +236,9 @@ function createTimingInfo({
responseEnd: 0, responseEnd: 0,
transferSize: 0, transferSize: 0,
encodedBodySize: 0, encodedBodySize: 0,
decodedBodySize: 0 decodedBodySize: 0,
deliveryType: '',
responseStatus: 200
}`); }`);
assert(resource instanceof PerformanceEntry); assert(resource instanceof PerformanceEntry);
@ -252,6 +266,9 @@ function createTimingInfo({
initiatorType, initiatorType,
customGlobal, customGlobal,
cacheMode, cacheMode,
{},
200,
'',
); );
assert(resource instanceof PerformanceEntry); assert(resource instanceof PerformanceEntry);
@ -307,6 +324,9 @@ function createTimingInfo({
initiatorType, initiatorType,
customGlobal, customGlobal,
cacheMode, cacheMode,
{},
200,
''
); );
assert(resource instanceof PerformanceEntry); assert(resource instanceof PerformanceEntry);

View File

@ -34,10 +34,19 @@ const cacheMode = '';
async function main() { async function main() {
performance.setResourceTimingBufferSize(1); performance.setResourceTimingBufferSize(1);
performance.markResourceTiming(createTimingInfo(1), requestedUrl, initiatorType, globalThis, cacheMode); const args = [
requestedUrl,
initiatorType,
globalThis,
cacheMode,
{}, // body info
200,
'',
];
performance.markResourceTiming(createTimingInfo(1), ...args);
// Trigger a resourcetimingbufferfull event. // Trigger a resourcetimingbufferfull event.
performance.markResourceTiming(createTimingInfo(2), requestedUrl, initiatorType, globalThis, cacheMode); performance.markResourceTiming(createTimingInfo(2), ...args);
performance.markResourceTiming(createTimingInfo(3), requestedUrl, initiatorType, globalThis, cacheMode); performance.markResourceTiming(createTimingInfo(3), ...args);
assert.strictEqual(performance.getEntriesByType('resource').length, 1); assert.strictEqual(performance.getEntriesByType('resource').length, 1);
// Clear resource timings on resourcetimingbufferfull event. // Clear resource timings on resourcetimingbufferfull event.
@ -65,10 +74,10 @@ async function main() {
performance.clearResourceTimings(); performance.clearResourceTimings();
performance.setResourceTimingBufferSize(1); performance.setResourceTimingBufferSize(1);
performance.markResourceTiming(createTimingInfo(4), requestedUrl, initiatorType, globalThis, cacheMode); performance.markResourceTiming(createTimingInfo(4), ...args);
// Trigger a resourcetimingbufferfull event. // Trigger a resourcetimingbufferfull event.
performance.markResourceTiming(createTimingInfo(5), requestedUrl, initiatorType, globalThis, cacheMode); performance.markResourceTiming(createTimingInfo(5), ...args);
performance.markResourceTiming(createTimingInfo(6), requestedUrl, initiatorType, globalThis, cacheMode); performance.markResourceTiming(createTimingInfo(6), ...args);
// Increase the buffer size on resourcetimingbufferfull event. // Increase the buffer size on resourcetimingbufferfull event.
await new Promise((resolve) => { await new Promise((resolve) => {
@ -96,10 +105,10 @@ async function main() {
performance.clearResourceTimings(); performance.clearResourceTimings();
performance.setResourceTimingBufferSize(2); performance.setResourceTimingBufferSize(2);
performance.markResourceTiming(createTimingInfo(7), requestedUrl, initiatorType, globalThis, cacheMode); performance.markResourceTiming(createTimingInfo(7), ...args);
performance.markResourceTiming(createTimingInfo(8), requestedUrl, initiatorType, globalThis, cacheMode); performance.markResourceTiming(createTimingInfo(8), ...args);
// Trigger a resourcetimingbufferfull event. // Trigger a resourcetimingbufferfull event.
performance.markResourceTiming(createTimingInfo(9), requestedUrl, initiatorType, globalThis, cacheMode); performance.markResourceTiming(createTimingInfo(9), ...args);
// Decrease the buffer size on resourcetimingbufferfull event. // Decrease the buffer size on resourcetimingbufferfull event.
await new Promise((resolve) => { await new Promise((resolve) => {

View File

@ -30,11 +30,18 @@ const initiatorType = '';
const cacheMode = ''; const cacheMode = '';
async function main() { async function main() {
const args = [
timingInfo,
requestedUrl,
initiatorType,
globalThis,
cacheMode,
];
// Invalid buffer size values are converted to 0. // Invalid buffer size values are converted to 0.
const invalidValues = [ null, undefined, true, false, -1, 0.5, Infinity, NaN, '', 'foo', {}, [], () => {} ]; const invalidValues = [ null, undefined, true, false, -1, 0.5, Infinity, NaN, '', 'foo', {}, [], () => {} ];
for (const value of invalidValues) { for (const value of invalidValues) {
performance.setResourceTimingBufferSize(value); performance.setResourceTimingBufferSize(value);
performance.markResourceTiming(timingInfo, requestedUrl, initiatorType, globalThis, cacheMode); performance.markResourceTiming(...args);
assert.strictEqual(performance.getEntriesByType('resource').length, 0); assert.strictEqual(performance.getEntriesByType('resource').length, 0);
performance.clearResourceTimings(); performance.clearResourceTimings();
} }
@ -42,9 +49,9 @@ async function main() {
await waitBufferFullEvent(); await waitBufferFullEvent();
performance.setResourceTimingBufferSize(1); performance.setResourceTimingBufferSize(1);
performance.markResourceTiming(timingInfo, requestedUrl, initiatorType, globalThis, cacheMode); performance.markResourceTiming(...args);
// Trigger a resourcetimingbufferfull event. // Trigger a resourcetimingbufferfull event.
performance.markResourceTiming(timingInfo, requestedUrl, initiatorType, globalThis, cacheMode); performance.markResourceTiming(...args);
assert.strictEqual(performance.getEntriesByType('resource').length, 1); assert.strictEqual(performance.getEntriesByType('resource').length, 1);
await waitBufferFullEvent(); await waitBufferFullEvent();
@ -56,14 +63,14 @@ async function main() {
performance.clearResourceTimings(); performance.clearResourceTimings();
assert.strictEqual(performance.getEntriesByType('resource').length, 0); assert.strictEqual(performance.getEntriesByType('resource').length, 0);
// Trigger a resourcetimingbufferfull event. // Trigger a resourcetimingbufferfull event.
performance.markResourceTiming(timingInfo, requestedUrl, initiatorType, globalThis, cacheMode); performance.markResourceTiming(...args);
// New entry is not added to the global buffer. // New entry is not added to the global buffer.
assert.strictEqual(performance.getEntriesByType('resource').length, 0); assert.strictEqual(performance.getEntriesByType('resource').length, 0);
await waitBufferFullEvent(); await waitBufferFullEvent();
// Apply a new buffer size limit // Apply a new buffer size limit
performance.setResourceTimingBufferSize(1); performance.setResourceTimingBufferSize(1);
performance.markResourceTiming(timingInfo, requestedUrl, initiatorType, globalThis, cacheMode); performance.markResourceTiming(...args);
assert.strictEqual(performance.getEntriesByType('resource').length, 1); assert.strictEqual(performance.getEntriesByType('resource').length, 1);
} }

View File

@ -23,14 +23,10 @@
"idlharness.any.js": { "idlharness.any.js": {
"fail": { "fail": {
"expected": [ "expected": [
"PerformanceResourceTiming interface: attribute deliveryType",
"PerformanceResourceTiming interface: attribute firstInterimResponseStart", "PerformanceResourceTiming interface: attribute firstInterimResponseStart",
"PerformanceResourceTiming interface: attribute responseStatus",
"PerformanceResourceTiming interface: attribute renderBlockingStatus", "PerformanceResourceTiming interface: attribute renderBlockingStatus",
"PerformanceResourceTiming interface: attribute contentType", "PerformanceResourceTiming interface: attribute contentType",
"PerformanceResourceTiming interface: resource must inherit property \"deliveryType\" with the proper type",
"PerformanceResourceTiming interface: resource must inherit property \"firstInterimResponseStart\" with the proper type", "PerformanceResourceTiming interface: resource must inherit property \"firstInterimResponseStart\" with the proper type",
"PerformanceResourceTiming interface: resource must inherit property \"responseStatus\" with the proper type",
"PerformanceResourceTiming interface: resource must inherit property \"renderBlockingStatus\" with the proper type", "PerformanceResourceTiming interface: resource must inherit property \"renderBlockingStatus\" with the proper type",
"PerformanceResourceTiming interface: resource must inherit property \"contentType\" with the proper type", "PerformanceResourceTiming interface: resource must inherit property \"contentType\" with the proper type",
"PerformanceResourceTiming interface: default toJSON operation on resource" "PerformanceResourceTiming interface: default toJSON operation on resource"

View File

@ -27,7 +27,7 @@ runner.setInitScript(`
finalNetworkResponseStartTime: 0, finalNetworkResponseStartTime: 0,
encodedBodySize: 0, encodedBodySize: 0,
decodedBodySize: 0, decodedBodySize: 0,
}, 'https://nodejs.org', '', global, ''); }, 'https://nodejs.org', '', global, '', {}, 200, '');
`); `);
runner.runJsTests(); runner.runJsTests();

View File

@ -25,7 +25,7 @@ runner.setInitScript(`
finalNetworkResponseStartTime: 0, finalNetworkResponseStartTime: 0,
encodedBodySize: 0, encodedBodySize: 0,
decodedBodySize: 0, decodedBodySize: 0,
}, 'https://nodejs.org', '', global, ''); }, 'https://nodejs.org', '', global, '', {}, 200, '');
`); `);
runner.runJsTests(); runner.runJsTests();