|
@@ -0,0 +1,79 @@
|
|
|
+/**
|
|
|
+ * Analyzes HTTP caching headers
|
|
|
+ *
|
|
|
+ * @see https://developers.google.com/speed/docs/best-practices/caching
|
|
|
+ */
|
|
|
+
|
|
|
+exports.version = '0.2.a';
|
|
|
+
|
|
|
+exports.module = function(phantomas) {
|
|
|
+ 'use strict';
|
|
|
+
|
|
|
+ var cacheControlRegExp = /max-age=(\d+)/;
|
|
|
+
|
|
|
+ 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;
|
|
|
+
|
|
|
+ // catch Expires and Pragma headers
|
|
|
+ case 'expires':
|
|
|
+ case 'pragma':
|
|
|
+ // and Varnish specific headers
|
|
|
+ case 'x-pass-expires':
|
|
|
+ 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);
|
|
|
+ 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.on('recv', function(entry, res) {
|
|
|
+ var ttl = getCachingTime(entry.url, entry.headers);
|
|
|
+
|
|
|
+ // 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', entry.url + ' cached for ' + ttl + ' s');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|