Skip to content

Commit

Permalink
Account for 64 KiB limit for beacon data
Browse files Browse the repository at this point in the history
  • Loading branch information
westonruter committed Feb 6, 2025
1 parent 68c6deb commit e28c197
Showing 1 changed file with 42 additions and 2 deletions.
44 changes: 42 additions & 2 deletions plugins/optimization-detective/detect.js
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,7 @@ export default async function detect( {
return;
}

// Finalize extensions.
if ( extensions.size > 0 ) {
/** @type {Promise[]} */
const extensionFinalizePromises = [];
Expand Down Expand Up @@ -740,6 +741,36 @@ export default async function detect( {
}
}

/*
* Now prepare the URL Metric to be sent as JSON request body.
*/

const maxBodyLengthKiB = 64;
const maxBodyLengthBytes = maxBodyLengthKiB * 1024;

// TODO: Consider adding replacer to reduce precision on numbers in DOMRect to reduce payload size.
const jsonBody = JSON.stringify( urlMetric );
const percentOfBudget =
( jsonBody.length / ( maxBodyLengthKiB * 1000 ) ) * 100;

/*
* According to the fetch() spec:
* "If the sum of contentLength and inflightKeepaliveBytes is greater than 64 kibibytes, then return a network error."
* This is what browsers also implement for navigator.sendBeacon(). Therefore, if the size of the JSON is greater
* than the maximum, we should avoid even trying to send it.
*/
if ( jsonBody.length > maxBodyLengthBytes ) {
if ( isDebug ) {
error(
`Unable to send URL Metric because it is ${ jsonBody.length.toLocaleString() } bytes, ${ Math.round(
percentOfBudget
) }% of ${ maxBodyLengthKiB } KiB limit:`,
urlMetric
);
}
return;
}

// Even though the server may reject the REST API request, we still have to set the storage lock
// because we can't look at the response when sending a beacon.
setStorageLock( getCurrentTime() );
Expand All @@ -751,7 +782,16 @@ export default async function detect( {
);

if ( isDebug ) {
log( 'Sending URL Metric:', urlMetric );
const message = `Sending URL Metric (${ jsonBody.length.toLocaleString() } bytes, ${ Math.round(
percentOfBudget
) }% of ${ maxBodyLengthKiB } KiB limit):`;

// The threshold of 50% is used because the limit for all beacons combined is 64 KiB, not just the data for one beacon.
if ( percentOfBudget < 50 ) {
log( message, urlMetric );
} else {
warn( message, urlMetric );
}
}

const url = new URL( restApiEndpoint );
Expand All @@ -769,7 +809,7 @@ export default async function detect( {
url.searchParams.set( 'hmac', urlMetricHMAC );
navigator.sendBeacon(
url,
new Blob( [ JSON.stringify( urlMetric ) ], {
new Blob( [ jsonBody ], {
type: 'application/json',
} )
);
Expand Down

0 comments on commit e28c197

Please sign in to comment.