Skip to content

Commit

Permalink
perf_hooks: add deliveryType and responseStatus fields
Browse files Browse the repository at this point in the history
PR-URL: nodejs#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>
  • Loading branch information
KhafraDev authored and bmeck committed Jun 22, 2024
1 parent 7c7fae4 commit 0d01896
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 24 deletions.
10 changes: 9 additions & 1 deletion doc/api/perf_hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,19 +246,26 @@ and can be queried with `performance.getEntries`,
observation is performed, the entries should be cleared from the global
Performance Timeline manually with `performance.clearMarks`.

### `performance.markResourceTiming(timingInfo, requestedUrl, initiatorType, global, cacheMode)`
### `performance.markResourceTiming(timingInfo, requestedUrl, initiatorType, global, cacheMode, bodyInfo, responseStatus[, deliveryType])`

<!-- YAML
added:
- v18.2.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][]
* `requestedUrl` {string} The resource url
* `initiatorType` {string} The initiator name, e.g: 'fetch'
* `global` {Object}
* `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._

Expand Down Expand Up @@ -1911,6 +1918,7 @@ dns.promises.resolve('localhost');
```

[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
[High Resolution Time]: https://www.w3.org/TR/hr-time-2
[Performance Timeline]: https://w3c.github.io/performance-timeline/
Expand Down
34 changes: 33 additions & 1 deletion lib/internal/perf/resource_timing.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ const kCacheMode = Symbol('kCacheMode');
const kRequestedUrl = Symbol('kRequestedUrl');
const kTimingInfo = Symbol('kTimingInfo');
const kInitiatorType = Symbol('kInitiatorType');
const kDeliveryType = Symbol('kDeliveryType');
const kResponseStatus = Symbol('kResponseStatus');

class PerformanceResourceTiming extends PerformanceEntry {
constructor(skipThrowSymbol = undefined, name = undefined, type = undefined) {
Expand Down Expand Up @@ -136,6 +138,16 @@ class PerformanceResourceTiming extends PerformanceEntry {
return this[kTimingInfo].encodedBodySize + 300;
}

get deliveryType() {
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
return this[kDeliveryType];
}

get responseStatus() {
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
return this[kResponseStatus];
}

toJSON() {
validateInternalField(this, kInitiatorType, 'PerformanceResourceTiming');
return {
Expand All @@ -160,6 +172,8 @@ class PerformanceResourceTiming extends PerformanceEntry {
transferSize: this.transferSize,
encodedBodySize: this.encodedBodySize,
decodedBodySize: this.decodedBodySize,
deliveryType: this.deliveryType,
responseStatus: this.responseStatus,
};
}
}
Expand All @@ -182,6 +196,8 @@ ObjectDefineProperties(PerformanceResourceTiming.prototype, {
transferSize: kEnumerableProperty,
encodedBodySize: kEnumerableProperty,
decodedBodySize: kEnumerableProperty,
deliveryType: kEnumerableProperty,
responseStatus: kEnumerableProperty,
toJSON: kEnumerableProperty,
[SymbolToStringTag]: {
__proto__: null,
Expand All @@ -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');

resourceTiming[kInitiatorType] = initiatorType;
Expand All @@ -200,6 +224,8 @@ function createPerformanceResourceTiming(requestedUrl, initiatorType, timingInfo
// The spec doesn't say to validate it in the class construction.
resourceTiming[kTimingInfo] = timingInfo;
resourceTiming[kCacheMode] = cacheMode;
resourceTiming[kDeliveryType] = deliveryType;
resourceTiming[kResponseStatus] = responseStatus;

return resourceTiming;
}
Expand All @@ -211,6 +237,9 @@ function markResourceTiming(
initiatorType,
global,
cacheMode,
bodyInfo,
responseStatus,
deliveryType = '',
) {
// https://w3c.github.io/resource-timing/#dfn-setup-the-resource-timing-entry
assert(
Expand All @@ -222,6 +251,9 @@ function markResourceTiming(
initiatorType,
timingInfo,
cacheMode,
bodyInfo,
responseStatus,
deliveryType,
);

enqueue(resource);
Expand Down
24 changes: 22 additions & 2 deletions test/parallel/test-perf-hooks-resourcetiming.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ function createTimingInfo({
initiatorType,
customGlobal,
cacheMode,
{},
200,
'',
);

assert(resource instanceof PerformanceEntry);
Expand Down Expand Up @@ -128,6 +131,9 @@ function createTimingInfo({
initiatorType,
customGlobal,
cacheMode,
{},
200,
'',
);

assert(resource instanceof PerformanceEntry);
Expand Down Expand Up @@ -155,6 +161,8 @@ function createTimingInfo({
assert.strictEqual(resource.encodedBodySize, 0);
assert.strictEqual(resource.decodedBodySize, 0);
assert.strictEqual(resource.transferSize, 0);
assert.strictEqual(resource.deliveryType, '');
assert.strictEqual(resource.responseStatus, 200);
assert.deepStrictEqual(resource.toJSON(), {
name: requestedUrl,
entryType: 'resource',
Expand All @@ -177,6 +185,8 @@ function createTimingInfo({
transferSize: 0,
encodedBodySize: 0,
decodedBodySize: 0,
responseStatus: 200,
deliveryType: '',
});
assert.strictEqual(util.inspect(performance.getEntries()), `[
PerformanceResourceTiming {
Expand All @@ -200,7 +210,9 @@ function createTimingInfo({
responseEnd: 0,
transferSize: 0,
encodedBodySize: 0,
decodedBodySize: 0
decodedBodySize: 0,
deliveryType: '',
responseStatus: 200
}
]`);
assert.strictEqual(util.inspect(resource), `PerformanceResourceTiming {
Expand All @@ -224,7 +236,9 @@ function createTimingInfo({
responseEnd: 0,
transferSize: 0,
encodedBodySize: 0,
decodedBodySize: 0
decodedBodySize: 0,
deliveryType: '',
responseStatus: 200
}`);

assert(resource instanceof PerformanceEntry);
Expand Down Expand Up @@ -252,6 +266,9 @@ function createTimingInfo({
initiatorType,
customGlobal,
cacheMode,
{},
200,
'',
);

assert(resource instanceof PerformanceEntry);
Expand Down Expand Up @@ -307,6 +324,9 @@ function createTimingInfo({
initiatorType,
customGlobal,
cacheMode,
{},
200,
''
);

assert(resource instanceof PerformanceEntry);
Expand Down
27 changes: 18 additions & 9 deletions test/parallel/test-performance-resourcetimingbufferfull.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,19 @@ const cacheMode = '';

async function main() {
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.
performance.markResourceTiming(createTimingInfo(2), requestedUrl, initiatorType, globalThis, cacheMode);
performance.markResourceTiming(createTimingInfo(3), requestedUrl, initiatorType, globalThis, cacheMode);
performance.markResourceTiming(createTimingInfo(2), ...args);
performance.markResourceTiming(createTimingInfo(3), ...args);
assert.strictEqual(performance.getEntriesByType('resource').length, 1);

// Clear resource timings on resourcetimingbufferfull event.
Expand Down Expand Up @@ -65,10 +74,10 @@ async function main() {

performance.clearResourceTimings();
performance.setResourceTimingBufferSize(1);
performance.markResourceTiming(createTimingInfo(4), requestedUrl, initiatorType, globalThis, cacheMode);
performance.markResourceTiming(createTimingInfo(4), ...args);
// Trigger a resourcetimingbufferfull event.
performance.markResourceTiming(createTimingInfo(5), requestedUrl, initiatorType, globalThis, cacheMode);
performance.markResourceTiming(createTimingInfo(6), requestedUrl, initiatorType, globalThis, cacheMode);
performance.markResourceTiming(createTimingInfo(5), ...args);
performance.markResourceTiming(createTimingInfo(6), ...args);

// Increase the buffer size on resourcetimingbufferfull event.
await new Promise((resolve) => {
Expand Down Expand Up @@ -96,10 +105,10 @@ async function main() {

performance.clearResourceTimings();
performance.setResourceTimingBufferSize(2);
performance.markResourceTiming(createTimingInfo(7), requestedUrl, initiatorType, globalThis, cacheMode);
performance.markResourceTiming(createTimingInfo(8), requestedUrl, initiatorType, globalThis, cacheMode);
performance.markResourceTiming(createTimingInfo(7), ...args);
performance.markResourceTiming(createTimingInfo(8), ...args);
// Trigger a resourcetimingbufferfull event.
performance.markResourceTiming(createTimingInfo(9), requestedUrl, initiatorType, globalThis, cacheMode);
performance.markResourceTiming(createTimingInfo(9), ...args);

// Decrease the buffer size on resourcetimingbufferfull event.
await new Promise((resolve) => {
Expand Down
17 changes: 12 additions & 5 deletions test/parallel/test-performance-resourcetimingbuffersize.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,28 @@ const initiatorType = '';
const cacheMode = '';

async function main() {
const args = [
timingInfo,
requestedUrl,
initiatorType,
globalThis,
cacheMode,
];
// Invalid buffer size values are converted to 0.
const invalidValues = [ null, undefined, true, false, -1, 0.5, Infinity, NaN, '', 'foo', {}, [], () => {} ];
for (const value of invalidValues) {
performance.setResourceTimingBufferSize(value);
performance.markResourceTiming(timingInfo, requestedUrl, initiatorType, globalThis, cacheMode);
performance.markResourceTiming(...args);
assert.strictEqual(performance.getEntriesByType('resource').length, 0);
performance.clearResourceTimings();
}
// Wait for the buffer full event to be cleared.
await waitBufferFullEvent();

performance.setResourceTimingBufferSize(1);
performance.markResourceTiming(timingInfo, requestedUrl, initiatorType, globalThis, cacheMode);
performance.markResourceTiming(...args);
// Trigger a resourcetimingbufferfull event.
performance.markResourceTiming(timingInfo, requestedUrl, initiatorType, globalThis, cacheMode);
performance.markResourceTiming(...args);
assert.strictEqual(performance.getEntriesByType('resource').length, 1);
await waitBufferFullEvent();

Expand All @@ -56,14 +63,14 @@ async function main() {
performance.clearResourceTimings();
assert.strictEqual(performance.getEntriesByType('resource').length, 0);
// Trigger a resourcetimingbufferfull event.
performance.markResourceTiming(timingInfo, requestedUrl, initiatorType, globalThis, cacheMode);
performance.markResourceTiming(...args);
// New entry is not added to the global buffer.
assert.strictEqual(performance.getEntriesByType('resource').length, 0);
await waitBufferFullEvent();

// Apply a new buffer size limit
performance.setResourceTimingBufferSize(1);
performance.markResourceTiming(timingInfo, requestedUrl, initiatorType, globalThis, cacheMode);
performance.markResourceTiming(...args);
assert.strictEqual(performance.getEntriesByType('resource').length, 1);
}

Expand Down
4 changes: 0 additions & 4 deletions test/wpt/status/resource-timing.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,10 @@
"idlharness.any.js": {
"fail": {
"expected": [
"PerformanceResourceTiming interface: attribute deliveryType",
"PerformanceResourceTiming interface: attribute firstInterimResponseStart",
"PerformanceResourceTiming interface: attribute responseStatus",
"PerformanceResourceTiming interface: attribute renderBlockingStatus",
"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 \"responseStatus\" 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: default toJSON operation on resource"
Expand Down
2 changes: 1 addition & 1 deletion test/wpt/test-performance-timeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ runner.setInitScript(`
finalNetworkResponseStartTime: 0,
encodedBodySize: 0,
decodedBodySize: 0,
}, 'https://nodejs.org', '', global, '');
}, 'https://nodejs.org', '', global, '', {}, 200, '');
`);

runner.runJsTests();
2 changes: 1 addition & 1 deletion test/wpt/test-resource-timing.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ runner.setInitScript(`
finalNetworkResponseStartTime: 0,
encodedBodySize: 0,
decodedBodySize: 0,
}, 'https://nodejs.org', '', global, '');
}, 'https://nodejs.org', '', global, '', {}, 200, '');
`);

runner.runJsTests();

0 comments on commit 0d01896

Please sign in to comment.