Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf_hooks: add deliveryType and responseStatus fields #51589

Merged
merged 3 commits into from
May 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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:** `''`.
KhafraDev marked this conversation as resolved.
Show resolved Hide resolved

_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,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bodyInfo is currently unused because there's a possible bug where the resource timing is taking fields like encodedBodySize/decodedBodySize from the resource's timing info rather than its resource info.

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();
Loading