arnaucube
arnaucube/socketioMEANseedPublic Self-hosted repository
socketioMEANseed/server/node_modules/etag/index.js
server/node_modules/etag/index.jsRaw
  1/*!
  2 * etag
  3 * Copyright(c) 2014 Douglas Christopher Wilson
  4 * MIT Licensed
  5 */
  6
  7/**
  8 * Module exports.
  9 */
 10
 11module.exports = etag
 12
 13/**
 14 * Module dependencies.
 15 */
 16
 17var crc = require('crc').crc32
 18var crypto = require('crypto')
 19var Stats = require('fs').Stats
 20
 21/**
 22 * Module variables.
 23 */
 24
 25var crc32threshold = 1000 // 1KB
 26var NULL = new Buffer([0])
 27var toString = Object.prototype.toString
 28
 29/**
 30 * Create a simple ETag.
 31 *
 32 * @param {string|Buffer|Stats} entity
 33 * @param {object} [options]
 34 * @param {boolean} [options.weak]
 35 * @return {String}
 36 * @api public
 37 */
 38
 39function etag(entity, options) {
 40  if (entity == null) {
 41    throw new TypeError('argument entity is required')
 42  }
 43
 44  var isStats = isstats(entity)
 45  var weak = options && typeof options.weak === 'boolean'
 46    ? options.weak
 47    : isStats
 48
 49  // support fs.Stats object
 50  if (isStats) {
 51    return stattag(entity, weak)
 52  }
 53
 54  if (typeof entity !== 'string' && !Buffer.isBuffer(entity)) {
 55    throw new TypeError('argument entity must be string, Buffer, or fs.Stats')
 56  }
 57
 58  var hash = weak
 59    ? weakhash(entity)
 60    : stronghash(entity)
 61
 62  return weak
 63    ? 'W/"' + hash + '"'
 64    : '"' + hash + '"'
 65}
 66
 67/**
 68 * Determine if object is a Stats object.
 69 *
 70 * @param {object} obj
 71 * @return {boolean}
 72 * @api private
 73 */
 74
 75function isstats(obj) {
 76  // not even an object
 77  if (obj === null || typeof obj !== 'object') {
 78    return false
 79  }
 80
 81  // genuine fs.Stats
 82  if (obj instanceof Stats) {
 83    return true
 84  }
 85
 86  // quack quack
 87  return 'atime' in obj && toString.call(obj.atime) === '[object Date]'
 88    && 'ctime' in obj && toString.call(obj.ctime) === '[object Date]'
 89    && 'mtime' in obj && toString.call(obj.mtime) === '[object Date]'
 90    && 'ino' in obj && typeof obj.ino === 'number'
 91    && 'size' in obj && typeof obj.size === 'number'
 92}
 93
 94/**
 95 * Generate a tag for a stat.
 96 *
 97 * @param {Buffer} entity
 98 * @return {String}
 99 * @api private
100 */
101
102function stattag(stat, weak) {
103  var mtime = stat.mtime.toISOString()
104  var size = stat.size.toString(16)
105
106  if (weak) {
107    return 'W/"' + size + '-' + crc(mtime) + '"'
108  }
109
110  var hash = crypto
111    .createHash('md5')
112    .update('file', 'utf8')
113    .update(NULL)
114    .update(size, 'utf8')
115    .update(NULL)
116    .update(mtime, 'utf8')
117    .digest('base64')
118
119  return '"' + hash + '"'
120}
121
122/**
123 * Generate a strong hash.
124 *
125 * @param {Buffer} entity
126 * @return {String}
127 * @api private
128 */
129
130function stronghash(entity) {
131  if (entity.length === 0) {
132    // fast-path empty
133    return '1B2M2Y8AsgTpgAmY7PhCfg=='
134  }
135
136  return crypto
137    .createHash('md5')
138    .update(entity, 'utf8')
139    .digest('base64')
140}
141
142/**
143 * Generate a weak hash.
144 *
145 * @param {Buffer} entity
146 * @return {String}
147 * @api private
148 */
149
150function weakhash(entity) {
151  if (entity.length === 0) {
152    // fast-path empty
153    return '0-0'
154  }
155
156  var len = typeof entity === 'string'
157    ? Buffer.byteLength(entity, 'utf8')
158    : entity.length
159
160  if (len <= crc32threshold) {
161    // crc32 plus length when it's fast
162    // crc(str) only accepts utf-8 encoding
163    return len.toString(16) + '-' + crc(entity).toString(16)
164  }
165
166  // use md4 for long strings
167  return crypto
168    .createHash('md4')
169    .update(entity, 'utf8')
170    .digest('base64')
171}