phantomas
Version:
Headless Chromium-based web performance metrics collector and monitoring tool
102 lines (88 loc) • 3.41 kB
JavaScript
/**
* Analyzes HTTP caching headers
*/
;
module.exports = function (phantomas) {
var cacheControlRegExp = /max-age=(\d+)/;
// @see https://developers.google.com/speed/docs/best-practices/caching
function getCachingTime(url, headers) {
// false means "no caching"
var ttl = false,
headerName,
now = new Date(),
headerDate;
for (headerName in headers) {
var value = headers[headerName];
switch (headerName.toLowerCase()) {
// parse max-age=...
//
// max-age=2592000
// public, max-age=300, must-revalidate
case "cache-control":
var matches = value.match(cacheControlRegExp);
if (matches) {
ttl = parseInt(matches[1], 10);
}
break;
case "expires": // catch Expires and Pragma headers
case "pragma":
case "x-pass-expires": // and Varnish specific headers
case "x-pass-cache-control":
phantomas.incrMetric("oldCachingHeaders"); // @desc number of responses with old, HTTP 1.0 caching headers (Expires and Pragma)
phantomas.addOffender("oldCachingHeaders", {
url,
headerName,
value,
});
if (ttl === false) {
headerDate = Date.parse(value);
if (headerDate) ttl = Math.round((headerDate - now) / 1000);
}
break;
}
}
//console.log(JSON.stringify(headers)); console.log("TTL: " + ttl + ' s');
return ttl;
}
phantomas.setMetric("cachingNotSpecified"); // @desc number of responses with no caching header sent (no Cache-Control header)
phantomas.setMetric("cachingTooShort"); // @desc number of responses with too short (less than a week) caching time
phantomas.setMetric("cachingDisabled"); // @desc number of responses with caching disabled (max-age=0)
phantomas.setMetric("oldCachingHeaders");
phantomas.setMetric("cachingUseImmutable"); // @desc number of responses with a long TTL that can benefit from Cache-Control: immutable
phantomas.on("recv", (entry) => {
var ttl = getCachingTime(entry.url, entry.headers),
headerName;
// static assets
if (entry.isImage || entry.isJS || entry.isCSS) {
if (ttl === false) {
phantomas.incrMetric("cachingNotSpecified");
phantomas.addOffender("cachingNotSpecified", entry.url);
} else if (ttl <= 0) {
phantomas.incrMetric("cachingDisabled");
phantomas.addOffender("cachingDisabled", entry.url);
} else if (ttl < 7 * 86400) {
phantomas.incrMetric("cachingTooShort");
phantomas.addOffender("cachingTooShort", { url: entry.url, ttl });
} else {
// long TTL, suggest the use of Cache-Control: immutable (issue #683)
for (headerName in entry.headers) {
var value = entry.headers[headerName];
if (headerName.toLowerCase() === "cache-control") {
if (/,\s?immutable/.test(value) === false) {
phantomas.incrMetric("cachingUseImmutable");
phantomas.addOffender("cachingUseImmutable", {
url: entry.url,
ttl,
});
} else {
phantomas.log(
"caching: Cache-Control: immutable used for <%s>",
entry.url
);
}
}
}
}
}
});
};