/*! * fresh * Copyright(c) 2012 TJ Holowaychuk * Copyright(c) 2016-2017 Douglas Christopher Wilson * MIT Licensed */ 'use strict'; /** * RegExp to check for no-cache token in Cache-Control. * @private */ var CACHE_CONTROL_NO_CACHE_REGEXP = /(?:^|,)\s*?no-cache\s*?(?:,|$)/; /** * Module exports. * @public */ module.exports = fresh; /** * Check freshness of the response using request and response headers. * * @param {Object} reqHeaders * @param {Object} resHeaders * @return {Boolean} * @public */ function fresh(reqHeaders, resHeaders) { // fields var modifiedSince = reqHeaders['if-modified-since']; var noneMatch = reqHeaders['if-none-match']; // unconditional request if (!modifiedSince && !noneMatch) { return false; } // Always return stale when Cache-Control: no-cache // to support end-to-end reload requests // https://tools.ietf.org/html/rfc2616#section-14.9.4 var cacheControl = reqHeaders['cache-control']; if (cacheControl && CACHE_CONTROL_NO_CACHE_REGEXP.test(cacheControl)) { return false; } // if-none-match if (noneMatch && noneMatch !== '*') { var etag = resHeaders['etag']; if (!etag) { return false; } var etagStale = true; var matches = parseTokenList(noneMatch); for (var i = 0; i < matches.length; i++) { var match = matches[i]; if (match === etag || match === 'W/' + etag || 'W/' + match === etag) { etagStale = false; break; } } if (etagStale) { return false; } } // if-modified-since if (modifiedSince) { var lastModified = resHeaders['last-modified']; var modifiedStale = !lastModified || !(parseHttpDate(lastModified) <= parseHttpDate(modifiedSince)); if (modifiedStale) { return false; } } return true; } /** * Parse an HTTP Date into a number. * * @param {string} date * @private */ function parseHttpDate(date) { var timestamp = date && Date.parse(date); // istanbul ignore next: guard against date.js Date.parse patching return typeof timestamp === 'number' ? timestamp : NaN; } /** * Parse a HTTP token list. * * @param {string} str * @private */ function parseTokenList(str) { var end = 0; var list = []; var start = 0; // gather tokens for (var i = 0, len = str.length; i < len; i++) { switch (str.charCodeAt(i)) { case 0x20 /* */: if (start === end) { start = end = i + 1; } break; case 0x2c /* , */: list.push(str.substring(start, end)); start = end = i + 1; break; default: end = i + 1; break; } } // final token list.push(str.substring(start, end)); return list; }