/ node_modules / etag / index.js
index.js
  1  /*!
  2   * etag
  3   * Copyright(c) 2014-2016 Douglas Christopher Wilson
  4   * MIT Licensed
  5   */
  6  
  7  'use strict'
  8  
  9  /**
 10   * Module exports.
 11   * @public
 12   */
 13  
 14  module.exports = etag
 15  
 16  /**
 17   * Module dependencies.
 18   * @private
 19   */
 20  
 21  var crypto = require('crypto')
 22  var Stats = require('fs').Stats
 23  
 24  /**
 25   * Module variables.
 26   * @private
 27   */
 28  
 29  var toString = Object.prototype.toString
 30  
 31  /**
 32   * Generate an entity tag.
 33   *
 34   * @param {Buffer|string} entity
 35   * @return {string}
 36   * @private
 37   */
 38  
 39  function entitytag (entity) {
 40    if (entity.length === 0) {
 41      // fast-path empty
 42      return '"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"'
 43    }
 44  
 45    // compute hash of entity
 46    var hash = crypto
 47      .createHash('sha1')
 48      .update(entity, 'utf8')
 49      .digest('base64')
 50      .substring(0, 27)
 51  
 52    // compute length of entity
 53    var len = typeof entity === 'string'
 54      ? Buffer.byteLength(entity, 'utf8')
 55      : entity.length
 56  
 57    return '"' + len.toString(16) + '-' + hash + '"'
 58  }
 59  
 60  /**
 61   * Create a simple ETag.
 62   *
 63   * @param {string|Buffer|Stats} entity
 64   * @param {object} [options]
 65   * @param {boolean} [options.weak]
 66   * @return {String}
 67   * @public
 68   */
 69  
 70  function etag (entity, options) {
 71    if (entity == null) {
 72      throw new TypeError('argument entity is required')
 73    }
 74  
 75    // support fs.Stats object
 76    var isStats = isstats(entity)
 77    var weak = options && typeof options.weak === 'boolean'
 78      ? options.weak
 79      : isStats
 80  
 81    // validate argument
 82    if (!isStats && typeof entity !== 'string' && !Buffer.isBuffer(entity)) {
 83      throw new TypeError('argument entity must be string, Buffer, or fs.Stats')
 84    }
 85  
 86    // generate entity tag
 87    var tag = isStats
 88      ? stattag(entity)
 89      : entitytag(entity)
 90  
 91    return weak
 92      ? 'W/' + tag
 93      : tag
 94  }
 95  
 96  /**
 97   * Determine if object is a Stats object.
 98   *
 99   * @param {object} obj
100   * @return {boolean}
101   * @api private
102   */
103  
104  function isstats (obj) {
105    // genuine fs.Stats
106    if (typeof Stats === 'function' && obj instanceof Stats) {
107      return true
108    }
109  
110    // quack quack
111    return obj && typeof obj === 'object' &&
112      'ctime' in obj && toString.call(obj.ctime) === '[object Date]' &&
113      'mtime' in obj && toString.call(obj.mtime) === '[object Date]' &&
114      'ino' in obj && typeof obj.ino === 'number' &&
115      'size' in obj && typeof obj.size === 'number'
116  }
117  
118  /**
119   * Generate a tag for a stat.
120   *
121   * @param {object} stat
122   * @return {string}
123   * @private
124   */
125  
126  function stattag (stat) {
127    var mtime = stat.mtime.getTime().toString(16)
128    var size = stat.size.toString(16)
129  
130    return '"' + size + '-' + mtime + '"'
131  }