Projekt

Obecné

Profil

Stáhnout (2.65 KB) Statistiky
| Větev: | Revize:
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
}
(4-4/5)