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