twitst4tz

twitter statistics web application
Log | Files | Refs | README | LICENSE

index.js (2711B)


      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 }