second
[josuexyz/.git] / node_modules / fresh / index.js
1 /*!
2  * fresh
3  * Copyright(c) 2012 TJ Holowaychuk
4  * Copyright(c) 2016-2017 Douglas Christopher Wilson
5  * MIT Licensed
6  */
7
8 'use strict'
9
10 /**
11  * RegExp to check for no-cache token in Cache-Control.
12  * @private
13  */
14
15 var CACHE_CONTROL_NO_CACHE_REGEXP = /(?:^|,)\s*?no-cache\s*?(?:,|$)/
16
17 /**
18  * Module exports.
19  * @public
20  */
21
22 module.exports = fresh
23
24 /**
25  * Check freshness of the response using request and response headers.
26  *
27  * @param {Object} reqHeaders
28  * @param {Object} resHeaders
29  * @return {Boolean}
30  * @public
31  */
32
33 function fresh (reqHeaders, resHeaders) {
34   // fields
35   var modifiedSince = reqHeaders['if-modified-since']
36   var noneMatch = reqHeaders['if-none-match']
37
38   // unconditional request
39   if (!modifiedSince && !noneMatch) {
40     return false
41   }
42
43   // Always return stale when Cache-Control: no-cache
44   // to support end-to-end reload requests
45   // https://tools.ietf.org/html/rfc2616#section-14.9.4
46   var cacheControl = reqHeaders['cache-control']
47   if (cacheControl && CACHE_CONTROL_NO_CACHE_REGEXP.test(cacheControl)) {
48     return false
49   }
50
51   // if-none-match
52   if (noneMatch && noneMatch !== '*') {
53     var etag = resHeaders['etag']
54
55     if (!etag) {
56       return false
57     }
58
59     var etagStale = true
60     var matches = parseTokenList(noneMatch)
61     for (var i = 0; i < matches.length; i++) {
62       var match = matches[i]
63       if (match === etag || match === 'W/' + etag || 'W/' + match === etag) {
64         etagStale = false
65         break
66       }
67     }
68
69     if (etagStale) {
70       return false
71     }
72   }
73
74   // if-modified-since
75   if (modifiedSince) {
76     var lastModified = resHeaders['last-modified']
77     var modifiedStale = !lastModified || !(parseHttpDate(lastModified) <= parseHttpDate(modifiedSince))
78
79     if (modifiedStale) {
80       return false
81     }
82   }
83
84   return true
85 }
86
87 /**
88  * Parse an HTTP Date into a number.
89  *
90  * @param {string} date
91  * @private
92  */
93
94 function parseHttpDate (date) {
95   var timestamp = date && Date.parse(date)
96
97   // istanbul ignore next: guard against date.js Date.parse patching
98   return typeof timestamp === 'number'
99     ? timestamp
100     : NaN
101 }
102
103 /**
104  * Parse a HTTP token list.
105  *
106  * @param {string} str
107  * @private
108  */
109
110 function parseTokenList (str) {
111   var end = 0
112   var list = []
113   var start = 0
114
115   // gather tokens
116   for (var i = 0, len = str.length; i < len; i++) {
117     switch (str.charCodeAt(i)) {
118       case 0x20: /*   */
119         if (start === end) {
120           start = end = i + 1
121         }
122         break
123       case 0x2c: /* , */
124         list.push(str.substring(start, end))
125         start = end = i + 1
126         break
127       default:
128         end = i + 1
129         break
130     }
131   }
132
133   // final token
134   list.push(str.substring(start, end))
135
136   return list
137 }