2025-04-02 06:50:39 -04:00

138 lines
2.7 KiB
JavaScript

/*!
* 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;
}