Spaces:
Running
Running
/*! | |
* fresh | |
* Copyright(c) 2012 TJ Holowaychuk | |
* Copyright(c) 2016-2017 Douglas Christopher Wilson | |
* MIT Licensed | |
*/ | |
/** | |
* 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 | |
} | |